• Tutorial: Online Highscore med PHP, MYSQL och libcurl

    Hej!

    Jag har under en tid letat efter ett online highscore tutorial på nätet med MYSQL, PHP + något C/C++ lib för att kunna posta highscores från mina spel upp på internet. Tyvärr så hittade jag inget konkret som jag kände hade vad jag letade efter. Därför så har jag under några dagar researchat och skrivit ihop ett basic Online Highscore system, och tänkte dela med mig av erfarenheten. Nu kommer det säkert att finnas mycket som kan förbättras, eftersom jag är ganska "grön" på PHP & MYSQL. De som har förslag på förbättringar för att hindra MYSQL injections och annat, får gärna kommentera.


    EXEMPEL PÅ VAD MAN KAN GÖRA MED DET HELA


    Jag kodade ihop Android versionen med SDL och Curl, funkar fin fint!
    (videon är på engelska, eftersom fler än svenskar vill se det hela)


    INSTALLERA PHP & MYSQL

    Det första du behöver ha är PHP och MYSQL installerat, eller en domän som stödjer detta. Själv så använder jag den domän jag har på one.com.

    Om du ska ha allt lokalt på din egen server så rekommenderas denna youtube video:


    De flesta webhotell stödjer MYSQL databaser i dagens läge. Men om ditt webhotell inte säger något om det, och vill se ifall det stödjer MYSQL, så kan du enkelt själv skapa en .php fil med följande innehåll:

    Kod:
    <?
    phpinfo();
    ?>
    Sedan ladda upp den till domänen och se vad som finns.
    Ex http://www.minhemsida.se/phpinfo.php


    SKAPA MYSQL DATABAS


    Nästa steg är att skapa en MySQL Databas där du vill lagra datan. En del webhotell ger dig bara en databas, så då har du inte så mycket att välja på, mer än att använda den, och då kan du hoppa över detta steg.

    Vi skapar en .php fil och döper den till create_database.php

    Kod:
    <?
    $con = mysql_connect("localhost","anvandarnamn","losenord"); 
    if (!$con)
      {
      die('Kunde inte ansluta till: ' . mysql_error());
      }
    
    if (mysql_query("CREATE DATABASE game_highscore",$con))
      {
      echo "Databas skapad";
      }
    else
      {
      echo "Kunde inte skapa databas: " . mysql_error();
      }
    
    mysql_close($con);
    ?>
    Det första vi gör är att ansluta till MYSQL databasen med:

    Kod:
    $con = mysql_connect("localhost","anvandarnamn","losenord");
    Om du inte kör servern lokalt, så ersätt localhost med det servernamn på MYSQL servern du fått från ditt webhotell, samt användarnman och lösenord. Vi lagrar resultatet för anslutningen i $con för att se ifall vi lyckades att ansluta och logga in på servern.

    Kod:
    if (!$con)
      {
      die('Kunde inte ansluta till: ' . mysql_error());
      }
    om vi inte kunde ansluta, så anropar vi funktionen die(), och fyller i felmedelande, och låter MYSQL berätta varför vi inte kunde ansluta. Funktionen die() gör så att vi avslutar direkt, utan att gå vidare i koden.

    Kod:
    if (mysql_query("CREATE DATABASE game_highcsore",$con))
      {
      echo "Databas skapad";
      }
    else
      {
      echo "Kunde inte skapa databas: " . mysql_error();
      }
    mysql_query() är vad man använder för att utföra förfrågningar till MYSQL med.
    I detta fallet så skickar vi en förfrågan till MYSQL om att skapa en databas med namnet: highscore. Vi kollar även ifall vi lyckades skapa den eller inte.

    Kod:
    mysql_close($con);
    Till sist så avslutar vi anslutningen till MYSQL.


    SKAPA MYSQL TABLE


    Nästa steg är att skapa ett MYSQL Table där vi lagrar highscore informationen.

    Vi skapar och döper filen till create_table.php

    Kod:
    <?
    $con = mysql_connect("localhost","anvandarnamn","losenord");
    
    	if(!$con)
    	{
    		die( "Kunde inte ansluta till databasen");
    	}
    	
    mysql_select_db(game_highscore,$con) or die(mysql_error()); 
    
    $query = "CREATE TABLE test_highcsore
    (
    ID int NOT NULL AUTO_INCREMENT,
    PRIMARY KEY(ID),
    playername varchar(15),
    score int
    )";
    
    mysql_query($query,$con);
    mysql_close($con);
    ?>
    Vi hoppar över det som redan är förklarat och fortsätter med:

    Kod:
    mysql_select_db(game_highscore,$con) or die(mysql_error());
    Här väljer vi den databas vi skapade "game_highscore". Kör du webhotell som bara tillåter en databas som de skapat åt dig, så skriv in den här. Om det misslyckades, så anropar vi die() och visar varför det inte gick.

    Kod:
    $query = "CREATE TABLE test_highcsore
    (
    Här skapar vi en förfrågan till MYSQL om att skapa ett nytt table "test_highscore".

    Kod:
    ID int NOT NULL AUTO_INCREMENT,
    PRIMARY KEY(ID),
    Varje table ska ha ett primärt nyckelfält.
    En primär nyckel används för att identifiera unika rader i tabellen. Varje primär nyckel måste vara unik i ett table.
    Den primära nyckeln får inte vara null, eftersom databasmotorn kräver ett värde för att kunna hitta rätt i ett table.
    Därför sätter vi ID till NOT NULL.
    AUTO_INCREMENT innebär att den automatiskt ökar ID värdet med 1, varje gång en ny post läggs till.

    Kod:
    playername varchar(15),
    score int
    )";
    Vi skapar sedan två variabler för att spara spelarens namn, och hans poäng.

    Kod:
    mysql_query($query,$con);
    mysql_close($con);
    ?>
    Vi skickar sedan iväg förfrågan till MYSQL: mysql_query($query,$con); och avslutar sedan anslutningen.


    LÄGGA TILL DATA I HIGHSCORE TABLE


    Då vi skapat databasen och lagt till ett table i den, behöver vi nu ett php script för att lägga till data i Highscore table.

    Vi skapar en ny .php fil och kallar den insert_highscore.php.

    Kod:
    <?
    function getParam($name, $from, $link)
    {
     if (!array_key_exists($name, $from)) {return NULL;}
     $value = mysql_real_escape_string(urldecode(trim(strip_tags  ($from[$name]))), $link);
     return $value;
    }
    
    $con = mysql_connect("localhost","anvandarnamn","losenord");
    	if(!$con)
    	{
    		die( "Kunde inte ansluta till databasen");
    	}
    	
    @mysql_select_db(game_highscore) or die( "Kunde inte hitta databasen");
    
    $playername=getParam('playername', $_POST, $con);
    $score=getParam('score', $_POST, $con);
    
    $query = "INSERT INTO test_highscore VALUES ('','$playername','$score')";
    mysql_query($query);
    
    mysql_close($con);
    ?>
    Vi börjar med att titta på funktionen getParam($name, $from, $link). Hela syftet med denna funktionen är att hindra MYSQL injections, dvs hackare från att komma åt MYSQL databasen.

    Mer läsning om MYSQL injections hittar du här: http://webdesignskolan.se/php/mysql_...injections.php

    Kod:
    function getParam($name, $from, $link)
    {
     if (!array_key_exists($name, $from)) {return NULL;}
    array_key_exists() funktionen kollar efter en specifierad nyckel, och returnerar sant om den finns, och falskt om den inte finns. Vi kollar alltså så att nyckeln $name existerar i arrayen $from, gör den inte det, så returnerar vi NULL.

    Kod:
     $value = mysql_real_escape_string(urldecode(trim(strip_tags  ($from[$name]))), $link);
     return $value;
    }
    Vi skapar en ny variabel $value, och tar bort möjligheten för en MYSQL injection genom att "escapea", urlavkoda, trimma och ba bort taggar på strängen. Mycket på en gång, men nödvändigt.

    $link är den anslutning till serven du gjort. ( $con = mysql_connect("localhost","anvandarnamn","losenord "); )


    Förklaringar på de funktioner som appliceras:

    mysql_real_escape_string = gör om en sträng så att den utesluter MYSQL specifika kommando tecken.
    ( http://www.w3schools.com/php/func_my...ape_string.asp ).

    Följande tecken påverkas:
    • \x00
    • \n
    • \r
    • \
    • '
    • "
    • \x1a



    urldecode = avkodar %## från den givna strängen. Plus symbolen ('+') avkodas till ett mellanslag. ( http://php.net/manual/en/function.urldecode.php )

    trim = ta bort whitespaces and anra fördefinierade tecken fråm båda sidorna av en sträng.
    ( http://www.w3schools.com/php/func_string_trim.asp )

    strip_tags = tar bort HTML och PHP taggar från en sträng. ( http://php.net/manual/en/function.strip-tags.php )
    Kod:
    $playername=getParam('playername', $_POST, $con);
    Vi skapar sedan varabeln $playername och kollar efter en $_POST med namnet 'playername'.

    Kod:
    $score=getParam('score', $_POST, $con);
    Och kollar likadant efter en $_POST som heter 'score' som vi lagrar i $score.

    $_POST
    Den fördefinierade variabeln $_POST används för att samla värden från ett HTML form, skickat med metoden "post".
    Kod:
    $query = "INSERT INTO test_highscore VALUES ('','$playername','$score')";
    mysql_query($query);
    Vi skickar iväg förfrågan till MYSQL med "INSERT INTO" (sätt in) till "test_highscore" med "VALUES" (värdena) $score och $playername.

    Vi ser till att inte skriva in något i första posten i table, vilket är dess ID, varav vi hoppar över den med två stycken ' tecken i början: ('','$playername','$score')";

    Kod:
    mysql_close($con);
    ?>
    Till sist så avslutar vi MYSQL

    TOP TIO HIGHSCORE LISTAN


    Vi har skapat databas, ett highscore table, och ett php skript för att spara highscore i vårt highscore table. Det är nu dags att skriva ett skript som visar upp de tio bästa spelarna.

    Vi skapar en ny fil som heter show_highscore.php

    Kod:
    <?
    $con = mysql_connect("localhost","anvandarnamn","losenord");
    
    	if(!$con)
    	{
    		die( ""Kunde inte ansluta till databasen.");
    	}
    
    @mysql_select_db(game_highscore) or die( "Kunde inte hitta databasen.");
    $query="SELECT * FROM test_highscore ORDER BY score DESC LIMIT 0,10";
    
    $result=mysql_query($query);
    
    $num=mysql_numrows($result);
    
    echo "<b><center>TOP 10 HIGHSCORE</center></b><br><br>";
    ?>
    
    <center><table border="4" cellspacing="4" cellpadding="4"></center>
    <tr>
    <th><font face="Arial, Helvetica, sans-serif">Name</font></th>
    <th><font face="Arial, Helvetica, sans-serif">Score</font></th>
    </tr>
    
    <?
    $i=0;
    while ($i < $num) {
    
    $playername=mysql_result($result,$i,"playername");
    $score=mysql_result($result,$i,"score");
    
    ?>
    <tr>
    <td><font face="Arial, Helvetica, sans-serif"><? echo $playername;?></font></td>
    <td><font face="Arial, Helvetica, sans-serif"><? echo $score;?></font></td>
    </tr>
    
    <?
    $i++;
    }
    mysql_close();
    ?>
    </table>
    Som tidigare hoppar vi över det som redan är förklarat och går in på de nya bitarna.

    Kod:
    $query="SELECT * FROM test_highscore ORDER BY score DESC LIMIT 0,10";
    Här skickar vi en förfrågan till MYSQL om att:
    SELECT * FROM test_highscore = välja vår highscore table
    ORDER BY score = sortera efter poäng
    DESC = "Descending", minskande (högst först -> lägst)
    LIMIT 0,10 = börja på 0 (längst upp i listan, som nu är sorterad efter högst poäng), och gå 10 poster frammåt.

    Hade vi skrivit "LIMIT 2,6" så hade den börjat på post 2 och räknat framm till post 8: 3,4,5,6,7,8
    Vill du ha en top 50 lista så är det bara att ändra om till: LIMIT 0,50

    Kod:
    $result=mysql_query($query);
    Vi sparar sedan den sorterade highscore listan i variabeln $result.

    Kod:
    $num=mysql_numrows($result);
    följande hämtar vi sedan antal rader som highscore listan innehåller.

    Kod:
    echo "<b><center>TOP 10 HIGHSCORE</center></b><br><br>";
    ?>
    Vi skriver ut en fin tittel på vad vi ska representera.
    Vi lämnar PHP med ?> eftersom att vi ska lägga upp en HTML tabell där vi visar highscoren.

    Kod:
    <center><table border="4" cellspacing="4" cellpadding="4"></center>
    <tr>
    <th><font face="Arial, Helvetica, sans-serif">Name</font></th>
    <th><font face="Arial, Helvetica, sans-serif">Score</font></th>
    </tr>
    Först centrerar vi tabellen till mitten av webläsaren och bestämmer dess utseende:
    table border = hur mycket kant vi ska ha runt om
    cellspacing = utrymmet mellan cellerna
    cellpadding = utrymmet mella text och cellkanten.

    Vi skapar sedan en ny tabell rad med:
    <tr> = table row

    lägger sedan dit två stycken rubriker "Name" och "Score:
    <th> = table header
    <font face="Arial, Helvetica, sans-serif"> = 3st olika fonter, ifall en skulle saknas, kan den prova nästa i listan.

    Kod:
    <?
    $i=0;
    while ($i < $num) {
    
    $playername=mysql_result($result,$i,"playername");
    $score=mysql_result($result,$i,"score");
    
    ?>
    Vi loopar nu igenom highscorelistan, och hämtar spelarens namn och poäng.
    och går ut ur PHP läge för att fortsätta skriva i tabellen:

    Kod:
    <tr>
    <td><font face="Arial, Helvetica, sans-serif"><? echo $playername;?></font></td>
    <td><font face="Arial, Helvetica, sans-serif"><? echo $score;?></font></td>
    </tr>
    Vi skapar en ny rad i tabellen med <tr>.
    Skapar sedan en table data med <td>, väljer font, går sedan tillbaks till PHP läge och skriver ut spelarens namn, avslutar PHP läge, font och table data.
    Gör sedan samma sak för poängen och avslutar tabell raden med <tr>

    Kod:
    <?
    $i++;
    }
    Efter att ha skrivit ut spelaren, så går vi till nästa spelare i highscore listan.

    Kod:
    mysql_close();
    ?>
    </table>
    Till sist så avslutar vi anslutningen till MYSQL och avslutar tabellen.


    HTML SUBMIT FORM


    Det kan vara kul att testa och se så att det går att mata in information till ditt MYSQL highscore table med hjälp av ett HTML form. Detta kan du dock senare ta bort, eftersom vi ska skicka informationen från vårt spel med hjälp av libcurl.

    Vi skapar en ny fil som heter: enter_highscore.html

    Kod:
    <!DOCTYPE html>
    <html>
    <body>
    <form action="insert_highscore.php" method="post">
    Name: <input type="text" name="playername"><br>
    Score: <input type="text" name="score"><br>
    <input type="Submit">
    </form>
    
    </body>
    </html>
    Denna kod är ganska så självförklarande, så jag går inte in närmare och beskriver de olika bitarna i den. HTML koden var bara lite bonus till det hela.


    INSTALLERA / KOMPILERA LIBCURL


    Till att börja med behöver vi ta hem libcurl, vilket hittas på denna sida:

    http://curl.haxx.se/download.html

    Visual Studio 2010
    Såg även att det kunde vara problem med visual studio 2010, så jag länkar till en som verkar ha löst det.
    http://quantcorner.wordpress.com/201...visual-c-2010/

    Mingw
    För de som vill köra mingw (GCC för windows) så rekommenderar jag: http://tdm-gcc.tdragon.net/ Den lägger till PATH automatiskt för mingw så att man kan köra gcc och mingw32-make direkt i cmd.

    Letar man efter en libcurl mingw32 development version, får man leta länge, jag hittade ingen iaf. Men, eftersom att jag redan kompilerat libbet, så hittar du det här (version 7.26.0): http://dream-code.se/temp/curl-devel-7.27.0.rar

    Vill du kompilera det själv, finns en bra guide här: http://lenkite.blogspot.se/2007/11/q...lain-http.html (funkar bara om du har PATH satt till mingw)

    Då det står: "change to this directory and use" menar han bara katalogen du packade upp allt till t.ex: "curl-7.27.0"

    För att kompilera ett exempel i doc/examples/ så kör denna:

    gcc -DCURL_STATICLIB -I ../../ -L ../../lib simple.c -o simple.exe -lcurl -lws2_32 -lwinmm -lwldap32

    Det som saknades på hans hemsida för att få det att kompilera var -lwldap32

    HIGHSCORE FRÅN DITT SPEL TILL DATABASEN


    Då har vi kommit till sista delen i denna tutorial.

    Börja med att ladda upp alla .php filer vi skapat till din hemsida / din lokala server, samt enter_highscore.html.

    t.ex : www.dinhemsida.se/highscore/

    1. Börja med att skapa databasen genom att köra: http://www.dinhemsida.se/highscore/create_database.php
    2. Kör sedan:http://www.dinhemsida.se/highscore/create_table.php för att skapa tabellen att spara highscore i.
    3. Nu kan du testa HTML formet vi skrev tidigare:http://www.dinhemsida.se/highscore/enter_highscore.html
    4. Kör nu: http://www.dinhemsida.se/highscore/show_highscore.php för att se den information du skickade upp.


    Med libcurl medföljer det en del exempel, bland dem hittar vi http-post.c, vilket är den som vi vill modifiera för att ansluta till vårt insert_highscore.php, och posta "playername" och "score".

    Vi skapar en .c fil som heter: test_highscore.c
    Kod:
    #include <stdio.h>
    #include <curl/curl.h>
    
    int main(void)
    {
      CURL *curl;
      CURLcode res;
      char name[20];
      char msg[200];
      int score;
    
      /* In windows, this will init the winsock stuff */
      curl_global_init(CURL_GLOBAL_ALL);
    
      /* get a curl handle */
      curl = curl_easy_init();
    
      printf("Enter name:");
      scanf("%s",&name);
      printf("\nEnter Score:");
      scanf("%d",&score);
    
      sprintf(msg,"playername=%s&score=%d&project=curl",name,score);
    
      if(curl) {
        /* First set the URL that is about to receive our POST. This URL can
           just as well be a https:// URL if that is what should receive the
           data. */
        curl_easy_setopt(curl, CURLOPT_URL, "http://www.dinhemsida.se/highscore/insert_highscore.php");
        /* Now specify the POST data */
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS,msg);
        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if(res != CURLE_OK)
          fprintf(stderr, "curl_easy_perform() failed: %s\n",
                  curl_easy_strerror(res));
    
        /* always cleanup */
        curl_easy_cleanup(curl);
      }
      curl_global_cleanup();
      return 0;
    }
    Koden är ganska självförklarande, så jag går inte in närmare på den.

    Glöm inte att ta bort enter_highscore.html om allt funkar som det ska, den finns bara med för att enkelt testa så att allt funkar som det ska.
    Du vill ju inte att det ska vara allt för enkelt att sätta in highscore i tabellen för små hackers som sniffar runt på din sida.

    Kompilerar du med mingw så behöver du länka med -lcurl -lws2_32 -lwinmm -lwldap32

    Jag har inte själv testat med visual studio, men det borde inte vara några problem att kompilera och länka det där.

    TILL SIST


    Jag hoppas att detta Tutorial ska komma till nytta för några av er. Jag trodde själv att det skulle ta längre tid att få ihop det, men ibland förvånar man sig själv.

    Johan Forsblom

    .