Online highscore list

Monkey Forums/Monkey Code/Online highscore list

Xaron(Posted 2011) [#1]
Hi all,

here comes some code to create a very simple high score list. Please note that this is not cheat proven because you just can set the scores via the URL. Plus the php code is not safe against sql injections but I just want to keep it simple in the beginning. I will expand this simple example later.

First, the php code for the web server. You need PHP + MySQL. And you need MNet for Monkey: http://www.monkeycoder.co.nz/Community/posts.php?topic=725

Paste the following into phpMyAdmin to create the database:
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

CREATE TABLE IF NOT EXISTS `list` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL,
  `points` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;


Now you should have a database "list" which has just a name and points to store the values.

Here is the code which gets the highscore values from Monkey:
<?PHP
  mysql_connect( "localhost", "databaseuser", "password" )
    or die( "No connection: " . mysql_error() );

  mysql_select_db( "databasename" );
  $name = $_GET['name'];
  $score = $_GET['score'];
  $result = mysql_query( "INSERT INTO list (name, points) VALUES ('$name', '$score')" );
?>

Save this code as getscore.php and upload it to your webspace. Replace databaseuser, password and databasename with the real values you have set in your MySQL environment.

Here comes the code which displays the highscorelist:
<html>
  <head>
    <meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
    <title>Highscore</title>
  </head>

  <body>
    <table>
      <tr>
        <td>Rank</td>
        <td>Name</td>
        <td>Points</td>
      </tr>

<?PHP
  mysql_connect( "localhost", "databaseuser", "password" )
    or die( "No connection: " . mysql_error() );

  mysql_select_db( "databasename" );
  $result = mysql_query( "SELECT * FROM list ORDER BY points DESC" );

  $i = 1;
  while($row = mysql_fetch_array( $result, MYSQL_NUM ) )
  {
    printf( "<tr><td>%d.</td><td>%s</td><td>%d</td></tr>", $i, $row[1], $row[2] );
    $i++;
  }
  @mysql_free_result($result);
?>

    </table>
  </body>
</html>

Save this as highscorelist.php and upload it to your webspace as well. Replace databaseuser, password and databasename with the real values you have set in your MySQL environment.

Finally the Monkey code to set a new high score:
Local playerName:String = "xaron"
Local playerScore:Int = 1234
Local scoreString:String = "http://www.urltoyourspace.com/getscore.php?name=" + playerName + "&score=" + playerScore
mnet.HttpGet( scoreString )


That's all. :)

Example:
http://xaron.net/dl/monkey/highscore/highscorelist.php

If you want to put something into my test highscore list, just use this link:
xaron.net/dl/monkey/highscore/getscore.php?name=xaron&score=1234

Please note, if you click on the link for getscore.php you don't see anything because it just inserts new values into the db. You can do this from Monkey as well of course but you can see how cheating would be done with this simple aproach.


Neuro(Posted 2011) [#2]
Thanks so much for the quick example Xaron! But how would we be able to access the DB without using HTTPGET (for the purpose of displaying the scores in your game on the device instead of using a browser)? Also, are there anyway to prevent from potential cheating?


wmaass(Posted 2011) [#3]
In Xaron's example you could have getscore.php return a list of scores (you could simply echo a string) after it inserts the new score:

Then to get the result you would add something like:

Local playerName:String = "xaron"
Local playerScore:Int = 1234
Local scoreString:String = http://www.urltoyourspace.com/getscore.php?name=" + playerName + "&score=" + playerScore
Local resultString:String
resultString = mnet.HttpGet( scoreString )
Print resultString ' this is your high score list



Xaron(Posted 2011) [#4]
That would be possible using httpget as well. You just have to parse the output with Monkey. You get a string including all values. Of course you should alter the output (highscorelist.php) that way to don't send all this html overhead. Just a string separated with ",". You can use the Split command from Monkey.

Prevent cheating will be an extension. I will post some more stuff as well. You need some kind of secret keys or just have to create user accounts with passwords (so no anonymous anymore).


Xaron(Posted 2011) [#5]
@wmaas: Not exactly that way because getscore.php doesn't return the list:

Local playerName:String = "xaron"
Local playerScore:Int = 1234
Local scoreString:String = "http://www.urltoyourspace.com/getscore.php?name=" + playerName + "&score=" + playerScore
mnet.HttpGet( scoreString )
Local resultString:String
resultString = mnet.HttpGet( "http://www.urltoyourspace.com/highscorelist.php" )
Print resultString ' this is your high score list


Change highscorelist.php to
<?PHP
  mysql_connect( "localhost", "databaseuser", "password" )
    or die( "No connection: " . mysql_error() );

  mysql_select_db( "databasename" );
  $result = mysql_query( "SELECT * FROM list ORDER BY points DESC" );

  $i = 1;
  while($row = mysql_fetch_array( $result, MYSQL_NUM ) )
  {
    printf( "%s,%d,", $i, $row[1], $row[2] );
    $i++;
  }
  @mysql_free_result($result);
?>


Now you have a comma separated list with names and points.
Just do:
For Local value:String = Eachin resultString.Split( "," )
  Print value
Next



wmaass(Posted 2011) [#6]
Awesome Xaron, thanks for sharing.


Soap(Posted 2011) [#7]
IMHO, I don't see any way TO make this cheat proof. The website has to assume any initial contact is from the application for it to work, which someone can easily replicate with minor snooping of their network calls. This is not a real viable way of having a serious high score list unless you can somehow craft a decently secure auth system which doesn't seem possible with just HTTP calls.

You could make a super complex system a program could only navigate but that would make submitting take longer and possibly frustrating to the user if something goes wrong in transit.

Hopefully I'm wrong and there is a fairly easy way of keeping casual hackers from side-stepping the system, but I can't think of any way currently.


Xaron(Posted 2011) [#8]
Of course nothing is 100% safe against hacking attempts but you can at least make it unbreakable for the common script kid idiot. My idea would be to use some kind of session keys. The server (php) generates a random number which the client (Monkey) reads out via HttpGet. Now some fancy calculation is done and the result is send back to the server together with the highscore. The server receives the calculation result and performs the same calculation with the random number it has sent before. If both numbers match the entry is valid.

BTW: You can make this really, really safe using public/private key stuff the way online banking is done (and yes this works via Http(s)).


debug(Posted 2011) [#9]
hi guys, I've been working on something very close to this for a little while now, and although it's not free, it's currently pretty cheap:
http://www.geeksanon.com/store/index.html
As you can see it's using a simple windows exe at the moment to post and retrieve the scores, but this should be very easy to convert to Monkey code by someone in the know. check out the pdf file that you can download and review.
It uses normal HTTP GET commands only so is very simple and has the security mechanism built in via the gamekey parameter when you game is initially registered at http://www.geeksanon.com/scoreboard/index.html
Retrieval of scores is via www.geeksanon.com/scoreboard/GAMES/<gamename>/scoreboard.txt which you can see is a text file so it can either be viewed as a web page or downloaded to a local file if required.
e.g. http://www.geeksanon.com/scoreboard/GAMES/MyGame/scoreboard.txt
It is not 100% secure in that packet sniffing could reveal the gamekey, but I believe it is enough to stop 99.9% of the population from cheating.

@Xaron: sorry for highjacking your thread; just thought this was relevant.


Playniax(Posted 2011) [#10]
I'm going to include some kind of high score handler in the framework at some point and I have been working on it already. I just started out with PHP and JavaScript but I have a raw version working. I have both a SQL version and one that works with a simple txt file on the server.
Actually it's all not that hard but security is a concern. You can use some kind of encryption but then the key would be somewhere in your local code. Anyway I’m not so experienced with all these platforms yet to have a answer for it but I’m working on it.

I guess this high score stuff is important. I’m curious if Mark has planned more network support for the future. I think mobile apps/games need to communicate to servers somehow.


Neuro(Posted 2011) [#11]
A lot of good information here, thanks guys!


wmaass(Posted 2011) [#12]
Will be keeping an eye on this, this is going to be important for me. I am storing all kinds of game data in a MySql db for each user and it is possible that players will get prizes based on results - so cheating would be a very bad thing.


Xaron(Posted 2011) [#13]
Well if you combine user access via password and some kind of a good hash function this should become very safe.


Xaron(Posted 2011) [#14]
Ok, let's "harden" that code above and make it safer against SQL injections:

In getscore.php replace line:
$result = mysql_query( "INSERT INTO list (name, points) VALUES ('$name', '$score')" );


with:
if( is_numeric( $score ) )
{
  $query = sprintf("INSERT INTO list (name, points) VALUES ('%s', '%d')",
                    mysql_real_escape_string($name), $score);
  $result = mysql_query( $query );
}



Xaron(Posted 2011) [#15]
Alright, now let's do some cheat protection. For that we just generate a random number on the server and send it to the client. Both server and client perform a secret hash calculation. This is the weak link of this kind of protection. Use a very good hash function which don't give any hints about how it's generates (non linear stuff!).

New file sendrandom.php:
This generates the random number and sends it to the client.
<?PHP
  session_start();

  function make_seed()
  {
    list( $usec, $sec ) = explode( ' ', microtime() );
    return (float)$sec + ( (float)$usec * 100000 );
  }
  
  mt_srand( make_seed() );
  $rndValue = mt_rand();
  $_SESSION['rnd'] = $rndValue;
  printf( "%d", $rndValue );
?>


Altered file getscore.php:
<?PHP
  session_start();

  function hashMe( $rndValue )
  {
    $hashValue = $rndValue + 2;
    return $hashValue;
  }

  if( !isset( $_SESSION['rnd'] ) )
    die( "No hash set, cheating attempt?!" );

  mysql_connect( "localhost", "dbuser", "password" )
    or die( "No connection: " . mysql_error() );

  mysql_select_db( "dbname" );

  // Get the hash value from the client and so some fancy calculation
  $hashFromClient = $_GET['hash'];
  if( hashMe( $_SESSION['rnd'] ) == $hashFromClient )
  {
    $name = $_GET['name'];
    $score = $_GET['score'];
    if( is_numeric( $score ) )
    {
      $query = sprintf("INSERT INTO list (name, points) VALUES ('%s', '%d')",
                        mysql_real_escape_string($name), $score);
      $result = mysql_query( $query );
      $_SESSION['rnd'] = mt_rand(); // invalidate that number
    }
  }
?>


Take a closer look at the function hashMe. Fill this function with some real hash function stuff, prefer non linear calculations including prime numbers.
There is a new get value for the link as well which includes the calculation result:
http://xaron.net/dl/monkey/highscore/getscore.php?name=Xaron&score=12356&hash=1678529093

If the hash value is correct the entry will be inserted into the db, otherwise not.

Monkey code:
Function hashMe:Int( rndValue:Int )
  Local hashValue:Int = rndValue + 2
  Return hashValue
End Function

Local playerName:String = "xaron"
Local playerScore:Int = 1234
'Get the random number from the server
Local rndValue:Int = Int( mnet.HttpGet( "http://www.urltoyourspace.com/sendrandom.php" ) )

Local hashValue:Int = hashMe( rndValue )
Local scoreString:String = "http://www.urltoyourspace.com/getscore.php?name=" + playerName + "&score=" + playerScore + "&hash=" + String(hashValue)
mnet.HttpGet( scoreString )


Voila! This should give some cheat protection. Just choose a real hash function and this should be safe for normal security requirements.


wmaass(Posted 2011) [#16]
Thanks for this Xaron, very useful. I can use this with some other security measures I am thinking about doing such as moving some of the game logic to the server side. My app is a golf game so I think I might be able to move the shot calculation to the server, it might be fast enough - not sure. Users have to login to Facebook to play my game, this may or may not be another layer of security. What I worry about is people viewing the Javascript source and learning the URLS to the PHP files that handle DB work.

Do you know if it is possible for the PHP script to know that the HTTP request is coming from my app?


Xaron(Posted 2011) [#17]
No. You have to do that with session keys and/or private/public key sharing.


pinete(Posted 2011) [#18]
This post seems very interesting to me :)
just a little question more..

It would be possible, following the same concept to access to more complicated player information?
just in example, imagine I make an online game in which the player needs to save his money, status, level,... I mean, whatever... as an strategy game.

What should be the approach to this?

In my opinion this would be a key feature in order to make web games in which the player could continue a game sesion, what today it's very common. :)
thanks a lot!


Xaron(Posted 2011) [#19]
I need something like this as well and it is done a very similar way. You need a user database containing all users and have a session key for every user logged in. That's all. It would be good to have a md5 hash function, I might add this as well, because It's good for one way hashes anyway and you don't want to send passwords unencrypted at all.


Qcat(Posted 2011) [#20]
Fantastic Tutorial high scores is the next part of my game to work on.I was wondering if it would be possible to Facebook high scores. All I can find online at the moment is this.

http://ideveloper.kodingen.com/2010/facebook-friends-leaderboard/#Database

I would be very interested if any one has any more information.

Thanks
Adam


Xaron(Posted 2011) [#21]
Had a quick look and it shouldn't be too hard to add this. Will give it a try and will post a complete tutorial when done. The most difficult part will probably to add the original (native) virtual keyboards which we need for this highscore stuff anyway but I work on this as well. Will take some time....


wmaass(Posted 2011) [#22]
I've made some progress into getting my app up on Facebook and FB user data into the app, have not gotten to actually doing anything useful with it yet. I will post what I have a bit later.


Qcat(Posted 2011) [#23]
That sounds good. I never thought about the virtual keyboard been missing I must admit.


Paul - Taiphoz(Posted 2012) [#24]
Hows this going ?? did the facebook stuff ever come about ?


MikeHart(Posted 2012) [#25]
I am trying to implement MNET in a project. On IOS I get a result, on HTML5 on my IMac with FireFox I get always an "undefined" result. Even in the example that comes with the module. Why?

Edit: Same thing happens on Android and my HTC Desire. The result returned back is a NULL object.


MikeHart(Posted 2012) [#26]
Ok, for Android I needed the Internet permission. For HTML your Module seems not unusable.


JIM(Posted 2012) [#27]
As far as I know, on HTML5 you have to have the PHP and your MonkeyGame.html on the same domain. It's a protection measure implemented in browsers I think.


MikeHart(Posted 2012) [#28]
Do you mean in the same folder?


MikeHart(Posted 2012) [#29]
Ok, tried that. Put both in the same folder, didn't work.


MikeHart(Posted 2012) [#30]
Ok found out what I still needed. At the beginning of my PHP script, I needed to add this:

header("Access-Control-Allow-Origin: *");


Now Chrome and Firefox will retrieve data in HTML5.


Arabia(Posted 2014) [#31]
Hope I'm not bumping an old thread where I shouldn't be :)

Just mucking around with this after talking about using it for multiplayer turn based games - anyway, post #5 -> http://www.monkey-x.com/Community/posts.php?topic=812&post=6501 refers to just getting the data back as a string (i.e. no html) and it wasn't working. With my very very limited PHP skills, I think I've worked out that

printf( "%s,%d,", $i, $row[1], $row[2] );


should actually be

printf( "%s,%s,%d,", $i, $row[1], $row[2] );\


I've found some (over 100 in a series) really good PHP for beginners tutorials, so I will need to watch them to understand a lot of stuff that I don't, from what I see here I'm figuring %s and %d and used as either string (%s)? and numerical (%d)? is that right?

I'm thinking before getting in too deep (card game, multiplayer MySql server hosted) it would be best to start with something real simple - a multiplayer X & O's (naughts and crosses). Hopefully I can get something like that going and then see if we can get some Android or Tablet people to test it out.


Arabia(Posted 2014) [#32]
Still working away on this and I've run into a problem which is probably a simple fix. I'm using brl.httprequest and not mnet as in the example below:

Local playerName:String = "xaron"
Local playerScore:Int = 1234
Local scoreString:String = "http://www.urltoyourspace.com/getscore.php?name=" + playerName + "&score=" + playerScore
mnet.HttpGet( scoreString )


So my code looks like this:
Local regString:String = "http://monkeydb.netne.net/reguser.php?user=" + user + "&email=" + email + "&password=" + password
Local get_req:HttpRequest = New HttpRequest("GET", regString, Self)


But I get a "Unable to find overload for new (String,String,Game)" error. Any ideas? Can I use HttpRequest instead of mnet used in Xaron's original code?

EDIT: Problem solved, was missing a Implements IOnHttpRequestComplete from my Game Class