GameMaker Solution for coding and hosting user generated levels

RyanC

Member
Hi All,

Does anyone know the easiest way to set up a solution for hosting users levels so they can download, play, and rate each others levels.

My game currently exports user generated levels as a string so the solution only needs to be able to store these strings.

I was hoping to use something like the http_get function but I'd also like the users to be able to rate each others levels.

Will this require SQL programming or perhaps someone knows an online service that can provide this kind of functionality?

Any help appreciated!
 

marasovec

Member
You need a database to store the data somewhere. I can recommend you 000webhost because they will give you a 1GB database for free :p
It's not really about SQL as much as PHP.

For example heres my code to upload, show and download levels

Code:
/*
WHAT can be
"-2" as upload
"-1" as show levels
"any number higher than 0 as a level ID" as download
*/
var dbinput = "&name="+string(level_name)&data="+string(level_data));
global.post = http_post_string("https://name_of_the_site.000webhostapp.com/?what="+string(WHAT), dbinput);
index.php
Code:
    $h = 'localhost'; // host
    $p = '123456789' // password
    $d = 'idxxxxx_database'; // database ID

    if ($_GET['what'] == -2) // if "what" is "-2" it will upload a level
        {
        $data = $_POST["data"]; // get data from the http_post
        $name = $_POST["name"]; // get level name the from http_post
 
        $conn = mysqli_connect($h, $d, $p, $d); // connect to the db
        if (!$conn) die(mysqli_connect_error()); // if there is an error show me
 
        if (!mysqli_query($conn,"INSERT INTO pure_levels (name,data) VALUES ('$name','$data')")) echo(mysqli_error($conn)); // upload the level data under "data" and name under "name" into a table called "pure_levels"
        else echo "Level   ".$name."   has been uploaded"; // return a message that it the level was uploaded
        }


    else if ($_GET['what'] == -1) // if "what" is "-1" the database will show all the levels
        {
        $conn = mysqli_connect($h, $d, $p, $d); // connect to the db
        if (!$conn) die(mysqli_connect_error()); // if there is an error show me
 
        $sql = "SELECT * FROM pure_levels ORDER BY id DESC"; // " * " means all the levels, pure_levels is the table where the data is stored
        $result = mysqli_query($conn, $sql);
 
        if (mysqli_num_rows($result) > 0) // if there are any levels uploaded
            {
            while($row = mysqli_fetch_assoc($result)) // get all the data
                {
                echo $row["id"]." ".$row["name"]."|"; // return all the data
                }
            }
        else echo "null"; // no levels? return "null"
        }


    else if ($_GET['what'] > 0) // if "what" is higher than 0 that means you posted an ID of the level you want to downlaod
        {
        $conn = mysqli_connect($h, $d, $p, $d); // connect to the db
        if (!$conn) die(mysqli_connect_error()); // if there is an error show me
        $id = $_GET["what"]; // get the ID from "what"
 
        $sql = "SELECT * FROM pure_levels WHERE id = $id"; // find the level with the same ID
        $result = mysqli_query($conn, $sql); // return a result
 
        if (mysqli_num_rows($result) > 0) if "result > 0" that means the level was found
            {
                $row = mysqli_fetch_assoc($result); // get the level
                echo $row["data"]; // return the level
            }
        }
 
Last edited:

RyanC

Member
You need a database to store the data somewhere. I can recommend you 000webhost because they will give you a 1GB database for free :p
It's not really about SQL as much as PHP.

For example heres my code to upload, show and download levels

Code:
/*
WHAT can be
"-2" as upload
"-1" as show levels
"any number higher than 0 as a level ID" as download
*/
var dbinput = "&name="+string(level_name)&data="+string(level_data));
global.post = http_post_string("https://name_of_the_site.000webhostapp.com/?what="+string(WHAT), dbinput);
index.php
Code:
    $h = 'localhost'; // host
    $p = '123456789' // password
    $d = 'idxxxxx_database'; // database ID

    if ($_GET['what'] == -2) // if "what" is "-2" it will upload a level
        {
        $data = $_POST["data"]; // get data from the http_post
        $name = $_POST["name"]; // get level name the from http_post
 
        $conn = mysqli_connect($h, $d, $p, $d); // connect to the db
        if (!$conn) die(mysqli_connect_error()); // if there is an error show me
 
        if (!mysqli_query($conn,"INSERT INTO pure_levels (name,data) VALUES ('$name','$data')")) echo(mysqli_error($conn)); // upload the level data under "data" and name under "name" into a table called "pure_levels"
        else echo "Level   ".$name."   has been uploaded"; // return a message that it the level was uploaded
        }


    else if ($_GET['what'] == -1) // if "what" is "-1" the database will show all the levels
        {
        $conn = mysqli_connect($h, $d, $p, $d); // connect to the db
        if (!$conn) die(mysqli_connect_error()); // if there is an error show me
 
        $sql = "SELECT * FROM pure_levels ORDER BY id DESC"; // " * " means all the levels, pure_levels is the table where the data is stored
        $result = mysqli_query($conn, $sql);
 
        if (mysqli_num_rows($result) > 0) // if there are any levels uploaded
            {
            while($row = mysqli_fetch_assoc($result)) // get all the data
                {
                echo $row["id"]." ".$row["name"]."|"; // return all the data
                }
            }
        else echo "null"; // no levels? return "null"
        }


    else if ($_GET['what'] > 0) // if "what" is higher than 0 that means you posted an ID of the level you want to downlaod
        {
        $conn = mysqli_connect($h, $d, $p, $d); // connect to the db
        if (!$conn) die(mysqli_connect_error()); // if there is an error show me
        $id = $_GET["what"]; // get the ID from "what"
 
        $sql = "SELECT * FROM pure_levels WHERE id = $id"; // find the level with the same ID
        $result = mysqli_query($conn, $sql); // return a result
 
        if (mysqli_num_rows($result) > 0) if "result > 0" that means the level was found
            {
                $row = mysqli_fetch_assoc($result); // get the level
                echo $row["data"]; // return the level
            }
        }
The service looks good, I wanted to let players rate each others levels though, it still looks like they just give you the server space, does it required a lot of setup for the database?
 

marasovec

Member
The db is easy to set up. You just have to pay attention to select correct data types
EDIT
Also the rating system wouldn't be hard to make. You need to store 2 more values. "rating" and "times_voted"
You post your rating and and ID of the level you want to rate, the db will select the level, get the current rating, it adds your rating to the current one and adds +1 to "times_voted" and the final value gets devided by the "times_voted" value and replaced in the db
 
Last edited:

RyanC

Member
The db is easy to set up. You just have to pay attention to select correct data types
EDIT
Also the rating system wouldn't be hard to make. You need to store 2 more values. "rating" and "times_voted"
You post your rating and and ID of the level you want to rate, the db will select the level, get the current rating, it adds your rating to the current one and adds +1 to "times_voted" and the final value gets devided by the "times_voted" value and replaced in the db
This sounds promising, I have just a few questions:

Can this run directly from Game Maker without PHP extensions?

iOS / Android without any changes?

The ability to load lists of levels such as: My Levels, Top Rated, New unrated etc.
 

marasovec

Member
You don't need a PHP extension. Just make a text file, put the code in it and rename the file index.php
Then make a new website. Everything goes through the site.
open the site's file manager and delete a file called index.html and then upload the index.php file

I haven't tried it on iOS / Android because I use a free version of GMS1.4 but there shouldn't be a difference
 

RyanC

Member
You need a database to store the data somewhere. I can recommend you 000webhost because they will give you a 1GB database for free :p
It's not really about SQL as much as PHP.

For example heres my code to upload, show and download levels

Code:
/*
WHAT can be
"-2" as upload
"-1" as show levels
"any number higher than 0 as a level ID" as download
*/
var dbinput = "&name="+string(level_name)&data="+string(level_data));
global.post = http_post_string("https://name_of_the_site.000webhostapp.com/?what="+string(WHAT), dbinput);
index.php
Code:
    $h = 'localhost'; // host
    $p = '123456789' // password
    $d = 'idxxxxx_database'; // database ID

    if ($_GET['what'] == -2) // if "what" is "-2" it will upload a level
        {
        $data = $_POST["data"]; // get data from the http_post
        $name = $_POST["name"]; // get level name the from http_post
 
        $conn = mysqli_connect($h, $d, $p, $d); // connect to the db
        if (!$conn) die(mysqli_connect_error()); // if there is an error show me
 
        if (!mysqli_query($conn,"INSERT INTO pure_levels (name,data) VALUES ('$name','$data')")) echo(mysqli_error($conn)); // upload the level data under "data" and name under "name" into a table called "pure_levels"
        else echo "Level   ".$name."   has been uploaded"; // return a message that it the level was uploaded
        }


    else if ($_GET['what'] == -1) // if "what" is "-1" the database will show all the levels
        {
        $conn = mysqli_connect($h, $d, $p, $d); // connect to the db
        if (!$conn) die(mysqli_connect_error()); // if there is an error show me
 
        $sql = "SELECT * FROM pure_levels ORDER BY id DESC"; // " * " means all the levels, pure_levels is the table where the data is stored
        $result = mysqli_query($conn, $sql);
 
        if (mysqli_num_rows($result) > 0) // if there are any levels uploaded
            {
            while($row = mysqli_fetch_assoc($result)) // get all the data
                {
                echo $row["id"]." ".$row["name"]."|"; // return all the data
                }
            }
        else echo "null"; // no levels? return "null"
        }


    else if ($_GET['what'] > 0) // if "what" is higher than 0 that means you posted an ID of the level you want to downlaod
        {
        $conn = mysqli_connect($h, $d, $p, $d); // connect to the db
        if (!$conn) die(mysqli_connect_error()); // if there is an error show me
        $id = $_GET["what"]; // get the ID from "what"
 
        $sql = "SELECT * FROM pure_levels WHERE id = $id"; // find the level with the same ID
        $result = mysqli_query($conn, $sql); // return a result
 
        if (mysqli_num_rows($result) > 0) if "result > 0" that means the level was found
            {
                $row = mysqli_fetch_assoc($result); // get the level
                echo $row["data"]; // return the level
            }
        }

Is this really all that's required to load a level? I thought Game Maker would require http_get to get the level string, just wondering how http_post_string can retrun a level string into Game Maker?

Code:
WHAT can be
"-2" as upload
"-1" as show levels
"any number higher than 0 as a level ID" as download
*/
var dbinput = "&name="+string(level_name)&data="+string(level_data));
global.post = http_post_string("https://name_of_the_site.000webhostapp.com/?what="+string(WHAT), dbinput);
 

FrostyCat

Redemption Seeker
@marasovec, can you please NOT teach others to use PHP until you learn how to use a framework and how to keep trivial injection vulnerabilities out? Stuff like what you showed exacerbates the network security crisis of GMS-related servers and puts a huge black eye on GM as a whole. GET for submitting information, failing to URL-encode GET parameters, and building filthy unescaped SQL queries out of raw user input are all huge no-nos when it comes to working with server-side web programming languages and frameworks.

@RyanC, if you're fine with using PHP, please instead follow a proper tutorial like this one for building an API based on HTTP. You can find other similar ones online, and here are some samples of GML-side request code that I wrote several years ago. Post back here if you have any GML-side questions when working with those tutorials. If this means more sensible HTTP practices will get traction on GM user circles this way, I'll be happy to help.
 

RyanC

Member
@marasovec, can you please NOT teach others to use PHP until you learn how to use a framework and how to keep trivial injection vulnerabilities out? Stuff like what you showed exacerbates the network security crisis of GMS-related servers and puts a huge black eye on GM as a whole. GET for submitting information, failing to URL-encode GET parameters, and building filthy unescaped SQL queries out of raw user input are all huge no-nos when it comes to working with server-side web programming languages and frameworks.

@RyanC, if you're fine with using PHP, please instead follow a proper tutorial like this one for building an API based on HTTP. You can find other similar ones online, and here are some samples of GML-side request code that I wrote several years ago. Post back here if you have any GML-side questions when working with those tutorials. If this means more sensible HTTP practices will get traction on GM user circles this way, I'll be happy to help.
Not really too sharp with PHP, can anyone recommend an ext that would allow users to login, share and rate levels?
 

FrostyCat

Redemption Seeker
Not really too sharp with PHP, can anyone recommend an ext that would allow users to login, share and rate levels?
You could try something like this that allows you to sign in and share levels, but your use case is pretty specific. Custom work is almost certainly a given. Besides, if you already know GML, PHP is a pretty easy pick-up.
 
C

CombatCalamity

Guest
@FrostyCat
Hi,

Does one really need frameworks when working with PHP? Did you mean like Laravel and the likes?

Also, I understand the other 2, but not "failing to URL-encode GET parameters". Wouldn't GET parameters be fine without encoding/encryption if you were just to send request of some insensitive data?
 

FrostyCat

Redemption Seeker
Does one really need frameworks when working with PHP? Did you mean like Laravel and the likes?
Yes, I mean frameworks like Laravel, CodeIgniter, CakePHP and the like.

The choice is up to personal taste, and it is by no means mandatory. But PHP code written by people without framework experience tend to have a whole host of security problems due to needless improvisation. It shows in broad daylight on this topic.

Also, I understand the other 2, but not "failing to URL-encode GET parameters". Wouldn't GET parameters be fine without encoding/encryption if you were just to send request of some insensitive data?
First of all, encoding and encrypting are not interchangeable terms. Learn the difference. Here I'm mainly talking about failure to encode, not failure to encrypt. Not turning on HTTPS would be failure to encrypt, and while that's important, it's not a problem until the public deploy.

If the submitted parameter contains any characters with special meaning in URL encoding, failure to encode would cause the special meanings to trigger unexpectedly and potentially clobber adjacent data. There is also room for injecting unwanted parameters if this clobbering is intentional and hits the right place. I really don't care how non-sensitive the data is. If its goal is to reach the other side in one piece, then it must be encoded first. This is a matter of data integrity, not data security.

Furthermore, since RyanC specifically asked about "iOS and Android without any changes", not having HTTPS on in the final deploy is absolutely not kosher. The latest version of both of these OSes now forbid plain HTTP traffic on apps without special permission, and several HTTP cheapskates have been bitten publicly on the GMS 2 section already.
 

RizbIT

Member
are you saying its not safe to do the following say if user wanted to login to the game/app and you are passing the username and password to the php file on server to check if correct credentials:

Code:
post = http_post_string("http://mysite.uk/myapp/applogin.php","login="+url_encode(txt_username.value)+"&pass="+url_encode(txt_password.value)+"&rnd="+string(irandom(10000)))
 

FrostyCat

Redemption Seeker
are you saying its not safe to do the following say if user wanted to login to the game/app and you are passing the username and password to the php file on server to check if correct credentials:

Code:
post = http_post_string("http://mysite.uk/myapp/applogin.php","login="+url_encode(txt_username.value)+"&pass="+url_encode(txt_password.value)+"&rnd="+string(irandom(10000)))
Is this for real? This is a VTech-level blunder, right there. Someone hanging a shingle on their door should not have to ask about this.

The http:// at the beginning tells me it's unsecured and thus unsafe in public. The setup you described is only ever acceptable in a strictly local development setup, and even then not for anything that has the potential to go public. Anything that takes passwords or other confidential information in the open realm needs to be in HTTPS, and anyone who clings to HTTP will find themselves marginalized more and more. You have to be living under a rock and not have paid attention to news covering Firefox or Chrome over the past 5 years to think otherwise.

Also, this "client-generated 'hash'" fad at the end of your request is indicative of more problems. It tells me you learned this craft from amateur "instructors" on the GMC, not qualified web development professionals. The proper technique would have the server generate the token, and a much longer one to boot. Given that you employ the bad technique, you're likely to have also adopted the rest of their advice wholesale and made other mistakes. People who do this tend to inappropriately trust the client on other matters, or commit beginner-grade blunders with passwords like these:
  • Pre-hashing the password client-side before submitting (which in a database breach is almost like a plaintext password)
  • Using a reversible cipher to encrypt the password
  • Making up their own amateur ciphers and hashes instead of using industry-standard algorithms
  • Not hashing the password at all
  • Not salting the password hash
  • Using a "fast" hash for passwords (e.g. any of the SHAx or MDx series of hashes, especially MD5)
It's not just a matter of whether it's safe when it's in transit, it's also a matter of whether it's safe after arrival. And it's beyond sad how popular GM circle practices take too casual and cheap an approach for both.
 
Last edited:
Top