Jump to content


Photo

MySQL Demo for GM-HTML 5


  • Please log in to reply
53 replies to this topic

#1 tangibleLime

tangibleLime

    Lunatic

  • Global Moderators
  • 2520 posts
  • Version:GM:HTML5

Posted 25 September 2011 - 02:41 AM

GM-HTML 5 SQL Demo

With the advent of GM-HTML5, communicating with a MySQL database may prove an extremely useful resource for storing information. This is a simple guide to help you get started. For this example, we will be looking at a virtual basket of apples. Our database can hold a total of 16 apples - 8 green and 8 red.

If you're impatient or really don't care about how the process is done, check out the live example here.

Part 1 - Code

Step 0 - Basic Understanding of the Process

Like a lot of web development, this process will span multiple languages.

We will use GameMaker to execute a script from a JavaScript file. The JavaScript command loads a PHP file without reloading the page (AJAX). That PHP file has the ability to manipulate the MySQL database. After doing that, the PHP file can return data by displaying information needed to be returned. The JavaScript will accept that data and report it back to GameMaker.

This is not a guide for learning PHP, GML, JavaScript or SQL. It assumes that you have at least a basic understanding of all four languages. Without moderate PHP and SQL knowledge, manipulating the database may prove difficult. Refer to the PHP-MySQL guide at w3schools to learn more.

Step 1 - JavaScript

We will use JavaScript (AJAX) to execute a PHP file that contains the functions we wish to use. It returns the text that would be displayed on the PHP page if it was loaded by itself.

Parameters:
url - (String) Path to the PHP file with appended arguments.
async - (0 or 1) Run the command asynchronously (1) or synchronously (0).

function SQLCommand(url,async) {
   if (window.XMLHttpRequest) {
      xmlObject = new XMLHttpRequest();
   } else {
      xmlObject = new ActiveXObject("Microsoft.XMLHTTP");
   }
   
   xmlObject.open("GET",url,async);
   xmlObject.send(null);

   return xmlObject.responseText;
}

For the most part, you won't have to edit this function.

Step 2 - PHP

PHP will be used to actually speak with the MySQL database and carry out the desired actions. If a value is to be returned, the PHP function will simply echo the result onto the page, which the JavaScript function above will read and report back to GameMaker.

We'll start with two standard functions that we'll use to connect and close the connection to the database. I've removed the login information in my example for obvious reasons.

<?
   // database variables
   $dbHost = "localhost";
   $dbName = "<name here>";
   $dbUser = "<user here>";
   $dbPass = "<password here>";

   // database connection functions
   function dbConnect() {
      global $dbCon, $dbHost, $dbName, $dbUser, $dbPass;
      $dbCon = mysql_connect($dbHost,$dbUser,$dbPass);
      return mysql_select_db($dbName);
   }
   
   function dbClose() {
      global $dbCon, $dbHost, $dbName, $dbUser, $dbPass;
      mysql_close($dbCon);
      unset($dbCon);
   }
?>

Now need a table in the database, which is our virtual apple basket.

Our basket of apples will be a table consisting of three fields:
1) appleid (INT) - a unique identifier for the apple
2) color (TINYTEXT) - the apple color (green or red)
3) weight (INT) - the weight, in grams, of the apple

We can use PHP or phpMyAdmin to create the table. The following PHP code will suffice.

<?
   // connect to the database
   dbConnect();

   // create the table
   mysql_query("CREATE TABLE apples
      (
         appleid int NOT NULL AUTO_INCREMENT,
         PRIMARY KEY(appleid),
         color TINYTEXT,
         weight INT
      )
   ");

   // close the connection to the database
   dbClose();
?>

Now that we have a table to operate on and an easy way to connect to the database, we can define our custom functions. The PHP file accepts a standard argument, act, which decides which function to execute. Therefore, we have a switch statement that designates program control. Our example has three functions: Get apple lists, add an apple to a list and empty the entire basket.

<?
   // custom SQL functions
   switch($_GET['act']) {
      case "getApples":
         dbConnect();
         $qry = mysql_query("SELECT * FROM apples WHERE color='$_GET[color]'");
         $compile = "";
         while ($data = mysql_fetch_array($qry)) {
            if ($compile != "") $compile .= "|";
            $compile .= $data['weight'];
         }
         dbClose();
         echo $compile;
         
         break;
      
      case "addApple":
         dbConnect();
         mysql_query("INSERT INTO apples VALUES ('','$_GET[color]','$_GET[weight]')");
         dbClose();
         
         break;
      
      case "removeApples":
         dbConnect();
         mysql_query("TRUNCATE TABLE apples");
         dbClose();
         
         break;
   }
?>

With the argument of "getApples", the PHP file will use the additional argument of "color" to return a list in the form "x|y|z|..." of the weights of all apples of that color. The "addApple" command will insert a new apple into the basket with the specified color. The "removeApples" command will simply truncate the table, emptying the basket completely. Notice how in order to return a value, we simply echo the information to display it on the page. Users never actually see this information - it is read by the JavaScript AJAX and returned to GameMaker.

Step 3 - GameMaker

Now that we have PHP to talk to the database and JavaScript to talk to the PHP, we need the GameMaker generated HTML5 to talk to the JavaScript. In a new GM-HTML5 project, we create a new extension that employs the use of the function we wrote in the beginning of the demonstration. Refer to this tutorial by JacksonYarr if you have difficulty with this step. Assign the name of the function SQLCommand with two arguments (string, double), and a string return type.

After we've imported the AJAX JavaScript function, we're essentially all set. Use the SQLCommand(url,async) function to load the PHP file with your desired parameters.

For our apple basket example, the logical first course of action is to create a script that updates the lists of green and red apples.

// updateApples();
// retrieve the green and red apples from the database
global.applesGreen = string_parse(SQLCommand("sql.php?act=getApples&color=green",0),"|",true);
global.applesRed = string_parse(SQLCommand("sql.php?act=getApples&color=red",0),"|",true);

I'm using the string_parse script from GMLScripts.com, which I will provide the source to at the bottom of this post. The string_parse function takes the returned list of "x|y|z|..." and converts it into a ds_list containing the elements {x, y, z, ... }, much like the explode function of PHP.

Let's take a closer look at how GameMaker is getting the lists from the database. In particular, the list of green apples.

We call SQLCommand("sql.php?act=getApples&color=green",0) to get the raw list of green apples. You can see from the URL, the argument act is set to the function getApples, and the argument color is set to green. Refer back to our PHP function to see how these arguments direct program flow. We have the async parameter set to 0 in order to have the program wait for the SQL statement to go through before our program continues.

To add an apple to the basket, we simply use the addApples function. Remember, we don't want the basket to have more than 8 apples of each kind.

// add a green apple with a random weight into the database
if (ds_list_size(global.applesGreen) > 7) {
   show_message("You can only have eight green apples at a time!");
} else {
   randomWeight = random_range(100,250);
   SQLCommand("sql.php?act=addApple&color=green&weight="+randomWeight,0);
}

// update the apple counts
updateApples();

This code adds a green apple to the database, for reasons that I hope are now perfectly clear. Afterwards, we update the lists again to account for the new apple.

The procedure to empty the basket follows the same steps as the other SQL commands.

Part 2 - Example

View a live version of this example here. Note that multiple people may be accessing the example, so if you add one apple and two appear, it is because a different user added another.

It appears that exporting an editable project is not completely functional. When that is fixed, I will upload the editable example.
  • 12

#2 Mayhem Games

Mayhem Games

    Proud Kiwi

  • GMC Member
  • 955 posts
  • Version:GM:Studio

Posted 25 September 2011 - 02:45 AM

I was trying to do this yesterday but my fail PHP skills let me down :tongue: Thank you this will be very useful, this shows how powerful Game Maker HTML5 can be.
  • 0

#3 tangibleLime

tangibleLime

    Lunatic

  • Global Moderators
  • 2520 posts
  • Version:GM:HTML5

Posted 25 September 2011 - 02:52 AM

You don't need extensive PHP skills to manipulate a MySQL database. Check out the PHP-MySql tutorial at W3Schools to learn everything you'll need to know. (Added this into the main post).
  • 0

#4 xot

xot

    media multimixer

  • Global Moderators
  • 4650 posts
  • Version:GM:Studio

Posted 25 September 2011 - 02:54 AM

Very nice!
  • 0

#5 OMGCarlos

OMGCarlos

    GMC Member

  • GMC Member
  • 329 posts
  • Version:GM:HTML5

Posted 25 September 2011 - 02:59 AM

Awesome work and tutorial...I keep saying this but GM HTML5 FTW!
  • 0

#6 True Valhalla

True Valhalla

    ಠ_ಠ

  • Retired Staff
  • 4880 posts
  • Version:GM:Studio

Posted 25 September 2011 - 06:33 AM

Thanks for this tutorial, it will be very useful! +1 :)
  • 0

#7 BlaXun

BlaXun

    Slime Online Creator

  • GMC Member
  • 2616 posts
  • Version:GM:Studio

Posted 25 September 2011 - 09:02 AM

Yeah, this seems to be great...but it feels a lil slow in your example.

Edit:
I tried this right now...it doesn't work for me.

This is a weird behavior I get here:
http://it-liebig.com/SQLSample/index.html

I use everthing as you wrote, and in the first room there is simply a "controller" object which does the following in its create event:

show_message("runnning")

global.res = string_parse(SQLCommand("connect.php?act=Read",0),"|",true)

for (i=0; i<= ds_list_size(global.res);i+=1)
{
 show_message(ds_list_find_value(global.res,i))
}

show_message(string(ds_list_size(global.res)))

You will notice that the "running" is shown all the time... nothing else.
Well, missbehavior of GM ...or stupid developer (this means me...not u ;) )

Edit:
Nevermind, finally got it to work.
Thanks again

Edited by BlaXun, 25 September 2011 - 10:40 AM.

  • 0

#8 Manuel777

Manuel777

    InvaderGames

  • GMC Member
  • 2859 posts
  • Version:GM:Studio

Posted 25 September 2011 - 10:35 AM

Absolutely great! :)
  • 0

#9 Mark Overmars

Mark Overmars

    Game Maker Creator

  • YoYo Games Staff
  • 805 posts
  • Version:Unknown

Posted 25 September 2011 - 12:09 PM

Should be easy to use this to maintain an online highscore list for your game, wouldn't it? It would be useful if somebody did build this in such a way people can easily include it in their game.


If somebody has a webserver with enough room, you could even provide a single database for everybody to use to store the highscores for their games, that is, store all the different highscore lists in a single database. The game would just need to provide some unique key to access the data for his own game. Might not be so safe though.
  • 2

#10 BlaXun

BlaXun

    Slime Online Creator

  • GMC Member
  • 2616 posts
  • Version:GM:Studio

Posted 25 September 2011 - 12:14 PM

Yes, extremely helpful for online highscore.

However... I think this is far too much for a online highscore.

Simply writing to a text-file on the server would allready be enough for that :P

Anyway:
A HTML5 SQl-supported Chat made in Game Maker

http://it-liebig.com/SQLChat/index.html

Sorry, very uncomfortable right now... sometimes keys arent recognized... not keys supported besides characters (some functions in GM4HTML5 are currently broken, ex: keyboard_string, keyboard_lastchar)

Edit:
I am not rly sure if the ASYNC works, my chat sample should call the function async, but it still hangs and sometimes wont accept key input in the chat due to this.

Edited by BlaXun, 25 September 2011 - 12:46 PM.

  • 0

#11 Mark Overmars

Mark Overmars

    Game Maker Creator

  • YoYo Games Staff
  • 805 posts
  • Version:Unknown

Posted 25 September 2011 - 03:14 PM

How would you write to a textfile on the server? You would need something for that on the server side. And nothing is more logical than storing scores in a database I think.
  • 0

#12 tangibleLime

tangibleLime

    Lunatic

  • Global Moderators
  • 2520 posts
  • Version:GM:HTML5

Posted 25 September 2011 - 04:44 PM

I am not rly sure if the ASYNC works, my chat sample should call the function async, but it still hangs and sometimes wont accept key input in the chat due to this.

If async is set to 0, the program will wait for the command to be finished before continuing program flow. If async is set to 1, the program will not wait for the command to be finished before continuing program flow.

And nothing is more logical than storing scores in a database I think.

Absolutely!

MySQL can even sort the highscore lists and return the top (x) for you.

SELECT * FROM highscores ORDER BY score DESC LIMIT 0,10

The above code will return the top 10 scores in sorted order.

When I get some time, I'm plan on rewriting this entire tutorial. I'll also add a highscore tutorial since it would be much more useful. I'll also put emphesis on how this is a guide that shows the general procedure for talking to a server - modifying the PHP file can change the program from using SQL to textfiles, or anything else PHP can do.

Thanks for the responses!
  • 0

#13 BlaXun

BlaXun

    Slime Online Creator

  • GMC Member
  • 2616 posts
  • Version:GM:Studio

Posted 25 September 2011 - 04:48 PM

Yeah, I tried teh async option in both ways, but as you can see (My SQL powered Chat) it still doesnt recognize Keyboard_press sometimes...and I cant figure out the reason...there is not much code anyway...makes me wonder if the async call doesnt block somehow.
  • 0

#14 tangibleLime

tangibleLime

    Lunatic

  • Global Moderators
  • 2520 posts
  • Version:GM:HTML5

Posted 25 September 2011 - 04:50 PM

Try it without the whole SQL system; make sure it's not simply lag from HTML5.
  • 0

#15 paul23

paul23

    GMC Member

  • Global Moderators
  • 3361 posts
  • Version:GM8

Posted 25 September 2011 - 04:55 PM

$qry = mysql_query("SELECT * FROM apples WHERE color='$_GET[color]'");


What would happen if I fed it color as:

green';DROPTABLE apples;SELECT * FROM apples WHERE color='green



You might wish to add some extra about code injection :P
  • 0

#16 tangibleLime

tangibleLime

    Lunatic

  • Global Moderators
  • 2520 posts
  • Version:GM:HTML5

Posted 25 September 2011 - 05:03 PM

$qry = mysql_query("SELECT * FROM apples WHERE color='$_GET[color]'");


What would happen if I fed it color as:

green';DROPTABLE apples;SELECT * FROM apples WHERE color='green



You might wish to add some extra about code injection :P

Good point, I'll add in a security section outlining the importance of mysql_escape_string().
  • 0

#17 Razon

Razon

    GMC Member

  • GMC Member
  • 246 posts
  • Version:Unknown

Posted 25 September 2011 - 06:35 PM

Instead of mysql_escape_string, I recommend the use of mysql_real_escape_string. Because it makes sure to match the proper character set used with the SQL database, and makes sure that all of the characters are escaped correctly. The prior function is also deprecated and will throw a notice in newer PHP versions, on a side note you'll need a database connection established before you can use it.

You should also take into consideration whether magic quotes are enabled for php on the server or not, if so then you need to use stripslashes before escaping it with the mysql function so that there isn't any extra filtering left over on the text.

Edited by Razon, 25 September 2011 - 09:33 PM.

  • 1

#18 BlaXun

BlaXun

    Slime Online Creator

  • GMC Member
  • 2616 posts
  • Version:GM:Studio

Posted 25 September 2011 - 06:45 PM

Yes please.
I'd like some security infos too ^^,

And another thing...any idea how to prevent users from simply calling the php script directly and adding the variables with ridiculus highscores?
  • 0

#19 Alert Games

Alert Games

    GMC Member

  • GMC Member
  • 1088 posts
  • Version:GM8

Posted 25 September 2011 - 11:25 PM

@Blaxun: I have experience doing this as my website currently does this.

The only way to keep it as secure as possible would be to fetch the time from the server, set a degree of variability in the PHP file, and encrypt the time with the data send to check if it is a legit action.

Unfortunately, as you are calling javascript functions for both fetching the time, and for sending the information. Therefore, this only prevents easy hacking.

But on top of that, you need to encrypt the contents with an algorithm that does not use characters that may be escaped in a URL. This may be able to be avoided with careful work in the PHP, but I chose to create my own alogorithm. (plus, you MUST use a GML version of the algorithm for HTML5)

Heres a quick rundown:
1. Fetch the server time using a 'SQLcommand' in this case.
2. Combine the secure information sent into a string, including the time, and hash it using an alogorithm that does not interfere with the URL escaping.
3. 'Compile' the information to be sent. This includes the new hash of all the contents.
4. Have the PHP compare the hash, by hashing the contents the way you hashed it in GM.
5. Compare the time with the server time, checking if it is within the degree of variability.
6. Do the database stuff.
7. Send back the results.

On a side note, I wonder if you could use a memory hack or script hack on the game? If so, you would need to also hash important information like the score, lives, etc. or else people can cheat easily that way as well.

Edited by Alert Games, 25 September 2011 - 11:27 PM.

  • 0

#20 aiacono

aiacono

    GMC Member

  • New Member
  • 13 posts

Posted 26 September 2011 - 01:39 AM

You should use an extension like Fake Callback if you want async requests. Otherwise xhr.responseText isn't going to be available to your Game Maker objects.
  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users