using WWW to make a server for your game

Note: this is just my way of doing things feel free to adapt and redesign

So i saw a lot of post about how to connect to a server and using an aidl file(whatever that is) but honestly theres no need for anything other unity for connecting to your own server and database

So im making a tutorial to hopefully provide some insight

I will be using PHP for the backend and coding in C# but your welcome to use any language you are comfortable with

okay so what were gonna do first is create an empty gameobject in unity and name is SessionManager

Once we got that done lets go ahead and make a new C# script and name it SessionManager at this point we can go ahead and attach the script to the empty gameobject and go ahead and open up monodevelop

So first were gonna go ahead and set up and instance to our manager and set up some strings

public static SessionManager Instance{set;get;}

public string registerUrl;
public string loginURL;
public string saveURL;

private void Start(){
Instance = this;
}

so now in the editor you can set up the links to your page containing your scripts which we will handle later on
now were gonna make a check to see if the user has been registered

private void Start(){
        //set instance to this
        Instance = this;

        //check if player has a profile if not open registration
        if (!PlayerPrefs.HasKey ("RegisteredWithCloud")) {
            //if not activate registration screen
            RegistrationCourintine();
        } else {
            //if they are register login
            LoginCourintine();
        }
    }

now lets add one more string to the top of our script

private string Refid;

okay so now lets make a registration i will try to coment it the best i can but you should definitly read up on WWW if your having a hard time understanding

private IEnumerator RegisterUser(){
        //get form instance
        WWWForm form = new WWWForm ();

        //generate a string for user ref id
        string refid = Random.value.ToString() + "ff" + "z" + Mathf.Floor(Random.value).ToString() + Mathf.Floor(Random.value).ToString() + Random.value.ToString();

        //Post data
        form.AddField ("refid", refid);

        //upload to server
        WWW w = new WWW (registrationURL, form);

        //wait for response
        yield return w;

        //Handle response
        if (!string.IsNullOrEmpty (w.error)) {
            //something went wrong
            Debug.Log (w.error);
        } else {
            //register succesful
            Debug.Log ("registered successfully");

            //set refid for easy login
            PlayerPrefs.SetString ("refid", refid);

            //set user default data
            Refid = refid;

            //set user data so it doesnt return null
                        playerController.Instance.xp = 0;
          

            //user is now registered
            PlayerPrefs.SetString ("RegisteredWithCloud", "true");
        }
    }

So a lot going on in that code so you may has noticed that we called this RegisterUser but in our start function were calling RegistrationCourintine(); and LoginCourintine(); this is because you cannot directly call an IEnumerator like a void so you have to create a wrapper function to be able to call it so lets go ahead and create 4 wrappers

public string ReturnRefId(){
        return Refid;
    }

public void RegistrationCourintine(){
        StartCoroutine (RegisterUser ());
    }

    public void LoginCourintine(){
        StartCoroutine (LoginUser ());
    }

    public void SaveCourintine(int xp){
        StartCoroutine (SaveToServer (xp));
    }

the main one i wanna discuss here is saving when you are registering and logging in no data is sent to the server the server is creating and returning logic but when you are saving the game then you have to send logic to the server and as said before you cannot call the IEnumerator directly so you must also include your data calls inside the save wrapper as well

So now lets handle logging the user in

Now please bare with me it was like 3AM when i made this script and its a little messy but it works

private IEnumerator LoginUser(){
        //get user information
        string refid = PlayerPrefs.GetString ("refid");

        //new form instance
        WWWForm form = new WWWForm ();

        //create post data
        form.AddField("refid",refid);

        //download the data return by server
        WWW download = new WWW(loginURL,form);

        //wait for download to finish
        yield return download;

        if (!string.IsNullOrEmpty (download.error)) {
            //something went wrong
            Debug.Log ("Error downloading " + download.error);
        } else {
            //parse download.text for data

            //turn into an array of strings
            string[] data = download.text.Split((' '));

            /*now grab the data in format
             * refid
             * xp
             */

            //set username and password
            Refid = data[0];

            //set xp
            int xp;
            int.TryParse (data [1], out xp);
            playerController.Instance.SetXp (xp);

            Debug.Log ("Load Success");
        }
    }

I guess the key thing i wanna go over here is when downloading data it is returned in plain text because the only access method that you have in WWW is download.text but you can use TryParse to turn plain text into int but you also have to remember your gonna be sending a string of text back to the user not just one piece of data so you have to split the string up into an array so thats what Split does

on to our last piece of code within our SessionManager so weve been playing the game for a bit its not time to save our game

private IEnumerator SaveToServer(int xp){
        WWWForm form = new WWWForm ();

        form.AddField ("refid", Refid);
        form.AddField ("xp", xp);

        WWW w = new WWW (saveURL, form);

        yield return w;

        if (!string.IsNullOrEmpty (w.error)) {
            //something went wrong
            Debug.Log (w.error);
        } else {
            Debug.Log ("Save Successful");
        }
    }

So this script is pretty straight forward so im not gonna go into much detail

now everything would work if we had a backend but before then you should know registering and login will happen when the scene is loaded but saving doesnt have a call so whenever you wanna save the game you need to call this

SessionManager.Instance.SaveCourintine(xp);

remember that the wrapper accepts and argument that is an int and thats how you upload your data to the server

THE SERVER

Alright lets hop straight into it

so im not gonna explain any of the php code just gonna put it here for reference

So were gonna assume we have a table in our database that contains 3 fields

  • id
  • refid
  • xp

So first we need to connect to our database so lets make a script called connect.php and write the following(with your own info of course)

<?
session_start ();

$host = "localhost";
$user = "user";
$pass = "password";
$db = "my_db";

mysql_connect ($host, $user, $pass);
mysql_select_db ($db);
?>

So now lets create our register.php

<?
header('Access-Control-Allow-Origin: *');
require_once ("connect.php");
$refid = $_POST ['refid'];
$sql = "SELECT * FROM gameUsers WHERE refid='$refid'";
$query = mysql($sql);
$row = mysql_fetch_object($query);
$id = $row->id;
if(empty($id)){
        $sql3 = "INSERT INTO gameUsers SET refid='$refid'";
        $query3 = mysql_query($sql3) or die(mysql_error());
        echo "success";
}else{
     echo "account already exist with that refid";
}
?>

Now lets make the login.php

<?
header('Access-Control-Allow-Origin: *');
require_once ("connect.php");
$refid = $_POST ['refid'];
$sql = "SELECT * FROM gameUsers WHERE refid='$refid'";
$query = mysql_query ($sql);
$row = mysql_fetch_object ($query);
$refid = $row->refid;
$xp = $row->xp;
echo $refid . " " . $xp;
?>

now its really important that you echo each value back with a space if you remember we use a space in our Split so that we can parse the data

Now last but not least lets save our game with save.php now this one is pretty easy as well

<?
header('Access-Control-Allow-Origin: *');
require_once ("connect.php");
$refid = $_POST ['refid'];
$xp = $_POST ['xp'];
$sql = " UPDATE gameUsers SET xp='$xp' WHERE refid='$refid'";
$query = mysql_query ($sql) or die (mysql_error ());
?>

now everything is set up perfectly just link these pages in your SessionManager object and test it out!

Well i certainly hope that i was clear and helped out some people if you have any questions feel free to shoot me an inbox thank you

You really need to switch this to PDO… This looks like the depreciated stuff… I don’t blame you though, unless you know a lot about PHP, you aren’t going to know about PDO with Prepared Statements.
Not to mention you aren’t using any SQL Injection Prevention (From what I can see).

I mean hey, nothing wrong with trying to help support the community, but still.

Here’s a link to the system I just released the other day.

this is just a starters guide for the simple minded i just wanted to provide some insight obviously there are security flaws but since i built this upon automatic registration that eliminates the need for prepared statements since the end user never has direct access with the database you can use dead simple scripts even if someone had access to the url string it wouldnt do them any good because the game sets,loads, and saves the data

Actually, your PHP code is vulnerable to a SQL injection attack. For example, a hacker could POST their own custom string into the refid variable (on login.php, save.php, and register.php) and the hacker could do what ever they wanted to your SQL database. You really want to use PDO with prepared statements.

For example, a hacker could set refid to
'; DROP table if exists gameUsers;

You need to assume that at least some people are very mean, and will intentionally wreck what you are doing.

1 Like

Also, you cannot assume that nobody will know the URL of the PHP pages, because hackers will disassemble Unity games. Once they disassemble your game, they will know the URL of the PHP pages. Then they will launch a variety of attacks against your PHP pages to see what is vulnerable. First attack on PHP pages will be SQL injection attempts.

2 Likes

As @ShilohGames mentioned. I tried to say the same exact thing, but perhaps he/she put it in better words.
I know you were meaning well, but the truth is, at this point in times anything but PDO with Prepared statements is practically depreciated because of security flaws. Which is why I made my free system to give out, and I don’t normally ever give free stuff out - ever. But I knew it was important because for some reason for the life of me, I can’t understand why people keep releasing depreciated stuff that has major problems. I’d rather release something with more security knowing I did it right and not giving people without even warning them that their databases can be destroyed.

you don’t need a URL to Inject an SQL Statement. All you need is a username or password field and they can just do

username: admin ‘or’ ‘1=1’ and they can get admin access.

using prepared statements is preferred because the actual SQL Code isn’t tampered with, it gets the whole SELECT username WHERE password stuff, and then AFTER it gets that, it them queries the username, password so the actual SQL code can’t be changed to Inject.

I’m not the best at explaining things, just doing. So sorry if I’m not making any sense.

i get it and im not saying its not flawed i know it is but this wasnt really a php tutorial this is how you can CONNECT to a server using unity this more to show how to use WWW then anything because not everyone uses php some people use asp and other languages so thats why i didnt go in depth explaining the PHP because its not the main focus of the post

I suspect this can be also done by traffic sniffing, because http connection sends target address as plaintext.

I think one decent alternative to php is python server.
For example, see:
http://webpy.org/

SqlAlchemy can be used for database access.

I get you man, I’m not hating on what you did. More so just wanted others to know before just jumping in and using it and them get their XP hacked lol.

When you give people code online for free, there is a chance some users might incorporate the code into their projects without doing a detailed security review, so it is always important to review security aspects before sharing the code online.

I realize that you are just trying to help and that the PHP code was not the main focus of your post, but that is still not a good reason to leave obvious SQL injection attack vulnerabilities in the PHP code that you shared. This is true regardless of the language chosen for the code examples. I am not trying to pick on PHP. PHP can be properly security using PDO and prepared SQL statements.

https://en.wikipedia.org/wiki/SQL_injection
https://en.wikipedia.org/wiki/Prepared_statement