Collecting telemetry

Collecting telemetry

I recently wrote about Subnautica’s development process, and how they handled player telemetry to streamline the development process. So I figured now would be the perfect time to show you how easy it is to actually send and store data – in this case object positioning – somewhere in a remote database.

“But Dave, I don’t have a private database for hosting this kind of information!” – Fear not, for I have the perfect solution for you. We are going to hack into Google Drive and pretend like we’re a Google Form!

“I hope this doesn’t breach the terms of service”

This way we can store pretty much anything, provided we convert it to a string first. Which is fine, because we’re going to download it as a bunch of strings when we use it anyway.

Step 1 is to create the script which will masquerade as the Google form. The version seen below is missing some key details, but we’ll get to that in a minute:

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class Submitter : MonoBehaviour
{
    string BASE_URL = "";

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKeyDown("space"))
        {
            StartCoroutine(Post(transform.position.x, transform.position.y, transform.position.z));
        }
    }

    IEnumerator Post(float _x, float _y, float _z)
    {
        WWWForm form = new WWWForm();
        form.AddField("", _x.ToString());
        form.AddField("", _y.ToString());
        form.AddField("", _z.ToString());
        byte[] rawData = form.data;

        using (UnityWebRequest www = UnityWebRequest.Post(BASE_URL, form))
        {
            yield return www.SendWebRequest();

            Debug.Log("Successfully sent POST request");

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                Debug.Log(www.uploadHandler.data);
            }
        }
    }
}

To summarize what you see here, these are the important details:

  • The string BASE_URL needs to be filled out, this is what we use to connect to the database with.
  • The IEnumerator Post takes the X-, Y- and Z-coordinates of the object as parameters. These are added to a WWWForm, which is what we will send off to the database.
  • The form.AddField calls have empty strings in them. These will have to be filled out as well.

Once you have the code, you will need to go into Google Drive and create a Form. You can name it whatever you want, but for each bit of data you want to store, you should create a “short answer” text field:

And since we’re doing the X-, Y and Z-coordinates of an object, your form should roughly look like this:

With this set up, we are ready to become the Hackerman and do some jankypank. First, click on the “Preview” icon. This should open up a new tab which previews the form:

We do this to generate a URL in the page source-code, which we will use to connect to the Google Drive. In the preview-tab, do:

  • Right-click > View Page Source

This will show the source code for the page, which is a garbled mess of markup tags. But if you press Ctrl +F and search for form action, you will arrive at the URL which we need:

Eeey

You must copy this URL and paste it as the value for BASE_URL in the Unity script:

string BASE_URL = "placeholderurlgoeshere.com";

With that we can connect to the database, but we still have to submit a form which the database will accept. To do so, we will have to find out how the text-fields in the Google Form are identified. In the Google Form, on each of the three text fields, do:

  • Right-click > Inspect

This will open up an element inspector in your browser, highlighting some of the markup code that defines the site. In the highlighted text, look for the following:

Each of the three text fields will have a different name value. The one pictured here is for the X-coordinate text field, so I will take this value, go into the Post IEnumerator in the script, and add it as the string value in the form.AddField call, where the x-coordinate is being stored:

form.AddField("entry.1763005839", _x.ToString());

Repeat this process for the other two text fields as well. Once you have done this, we’re good to go!

All that remains at this stage, is being able to view and edit the stored information, as well as download it. Going back to the original Google Form tab (the non-preview version), there is a “Responses” tab which you can click on. To this, then click the green button saying “Create Spreadsheet”:

Really blowing my budget on huge red arrows today

This will generate a spreadsheet which shows all of your collected data. Optimally, we’d want to create some code to retrieve this information for us, but that’s a topic which I’ll cover in the next telemetry tutorial.

When viewing the spreadsheet, it will be empty at first – logically, since we haven’t sent it any data yet. To create and send data, the script will have to be attached to a game object in Unity. So slap that script onto something – a cube, whatever – press play and then press the spacebar. Boom, the object’s coordinates are submitted to the sheet, which updates live for you to see:

The timestamp seen here is generated automatically, which is excellent when performing queries, if we want to sort anything by date.

Alternative solution:

In case you have money and do own an SQL database, submitting data to it will also be fairly easy. It won’t make you feel like hackerman, but at least your information is stored on your own server *cough*googleownstheplanet*cough*. While I won’t walk you through setting up your database, my friend Emil has created a nice .php-file which you can stuff into your database and let Unity connect to:

<?php

  if ($_POST) {
    $pos_x = $_POST["position_x"];
    $pos_y = $_POST["position_y"];
    $pos_z = $_POST["position_z"];

    //Create connection
    $conn = mysqli_connect("serveraddressgoeshere.com", "databasenamegoeshere", "passwordgoeshere", "databasenamegoeshereagainforsomereason");

    //Check connection
    if (!$conn) {
      die("Connection failed: " . mysqli_connect_error);
    }
      echo "Connected successfully to the DataBase! ";

        $sql = "INSERT INTO positions (x, y, z) VALUES ('" . $pos_x ."', '" . $pos_y ."' , '" . $pos_z ."')";

        if (mysqli_query($conn, $sql)) {
          echo "Added to DataBase successfully ";
        }
        else {
          echo "Error: " . $sql . "<br>" . mysqli_error($conn) . "<br>";
        }

        //Close connection to DB
        mysqli_close($conn);
  }

?>

Assuming that this is the format you use for your database post request (again, I won’t walk you through this, just be Hackerman and do the Google), then you can format the Unity script as follows:

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class Submitter : MonoBehaviour
{
    string BASE_URL = "serveraddressgoeshere.com";

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKeyDown("space"))
        {
            StartCoroutine(Post(transform.position.x, transform.position.y, transform.position.z));
        }
    }

    IEnumerator Post(float _x, float _y, float _z)
    {
        WWWForm form = new WWWForm();
        form.AddField("position_x", _x.ToString());
        form.AddField("position_y", _y.ToString());
        form.AddField("position_z", _z.ToString());
        byte[] rawData = form.data;

        using (UnityWebRequest www = UnityWebRequest.Post(BASE_URL, form))
        {
            yield return www.SendWebRequest();

            Debug.Log("Successfully sent POST request");

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                Debug.Log(www.uploadHandler.data);
            }
        }
    }
}

The only changes are to the BASE_URL and the form.Addfield string parameters. The form string parameters simply have to match the “$_POST” parameters specified at the beginning of the .php-file.

That’s it for now!

Comments are closed.