Game Maker Firestore for HTML5

Game Maker Firestore for Html5 is the web version of my Firebase Extension, allows you to do real-time database operations in your Html5 game.

The reason why I haven’t added this into the main Firebase library is that >> Web version of Firestore works a bit different than Mobile one, and setup process of the Firebase Javascript SDK is different than mobile. It is best to have it as a separate package.
Just a tip : It is fully compatible with Facebook Instant Games and filesize is greatly reduced to work in compliance with it. (See the Installation Step 1 below)

Demonstration

Please don’t spam messages and consume my daily message limit(Which is 50k). The design is not responsive and message order is bad. I didn’t work on it much. Other than that, lets chat 😀

Installation Process

Installation process is a lot easier than mobile one. There are three steps required for this.

1. Get Your Web App’s Configuration

At this point, you must create a Web Project in Firebase. It’s an easy step, no need to write it here, and you can find lots of tutorials about it. Now, after creation, head over to the Project Settings (gear wheel icon on top left of the page). At this point, select CDN Setting, and then we only need to copy our configuration(check the red-marked area below)

Why CDN Option?
We may download and include the javascript files along with the game as well, but it’ll be faster and better to fetch them from Firebase/Google servers. And in case you wish to export your game to Facebook Instant Games or an instant game platform, you sure need to fetch the files from a supported CDN.
//There may be less keys in your configuration. No worries!
var firebaseConfig = {
    apiKey: ".............................",
    authDomain: "................................",
    databaseURL: "................................",
    projectId: "................................",
    storageBucket: "................................",
    messagingSenderId: "................................",
    appId: "................................"
  };

You may want to save it to a text file, or write it on your notebook. We will use this in a moment.

2. Prepare Your Index.html File

Run your Game Maker game for one time. When you do it, Game Maker automatically creates your Html5 game files and opens a Micro Web Server. You can find your game’s index.html file from the Micro Web Server.

Copy the index.html file and paste it in your Desktop. We’ll do a little addition to it. You can open it with Notepad, or better with Sublime Text. You will have to insert the following two scripts inside the <body> tag

<script src="https://www.gstatic.com/firebasejs/6.3.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/6.3.1/firebase-firestore.js"></script>

Right into the body tag (Below is an example of my index.html file. DO NOT COPY THE WHOLE TEXT BELOW. YOUR INDEX.HTML IS UNIQUE). Scroll down to the below and see where I put my scripts(I marked the area with //////SEE WHERE I PUT MY SCRIPTS!!!!!!!!!!!) at 81st line.

<!DOCTYPE html>
<html lang="en">
    <head>
        <!-- Generated by GameMaker:Studio http://www.yoyogames.com/gamemaker/studio -->
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta http-equiv="pragma" content="no-cache"/>
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name ="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
        <meta charset="utf-8"/>

        <!-- Set the title bar of the page -->
        <title>Firestore Text</title>

        <!-- Set the background colour of the document -->
        <style>
            body {
              background: #0;
              color:#cccccc;
              margin: 0px;
              padding: 0px;
              border: 0px;
            }
            canvas {
                      image-rendering: optimizeSpeed;
                      -webkit-interpolation-mode: nearest-neighbor;
                      -ms-touch-action: none;
                      margin: 0px;
                      padding: 0px;
                      border: 0px;
            }
            :-webkit-full-screen #canvas {
                 width: 100%;
                 height: 100%;
            }
            div.gm4html5_div_class
            {
              margin: 0px;
              padding: 0px;
              border: 0px;
            }
            /* START - Login Dialog Box */
            div.gm4html5_login
            {
                 padding: 20px;
                 position: absolute;
                 border: solid 2px #000000;
                 background-color: #404040;
                 color:#00ff00;
                 border-radius: 15px;
                 box-shadow: #101010 20px 20px 40px;
            }
            div.gm4html5_cancel_button
            {
                 float: right;
            }
            div.gm4html5_login_button
            {
                 float: left;
            }
            div.gm4html5_login_header
            {
                 text-align: center;
            }
            /* END - Login Dialog Box */
            :-webkit-full-screen {
               width: 100%;
               height: 100%;
            }
        </style>
    </head>

    <body>
        <div class="gm4html5_div_class" id="gm4html5_div_id">
            <!-- Create the canvas element the game draws to -->
            <canvas id="canvas" width="1024" height="768" >
                     <p>Your browser doesn't support HTML5 canvas.</p>
            </canvas>
        </div>

//////SEE WHERE I PUT MY SCRIPTS!!!!!!!!!!!
        <script src="https://www.gstatic.com/firebasejs/6.2.4/firebase-app.js"></script>
        <script src="https://www.gstatic.com/firebasejs/6.2.4/firebase-firestore.js"></script>
        <!-- Run the game code -->
        <script type="text/javascript" src="html5game/Game Maker Firebase Analytics.js?YWEZB=1649489189"></script>
        <script>window.onload = GameMaker_Init;</script>
    </body>
</html>

Now you can save the file. Drag this file to Game Maker IDE, under Included Files area. Once you do that, now you can head over to Options > HTML5. In this window, head over to General tab, select index.html under Advanced area.

Initialize Your App

Preferably in a Game Start Event, create a Map and fill it with the keys & values you’ve noted above. Below, I’ve created mine. (I’ve changed the values of course)

var configMap = ds_map_create();
configMap[? "apiKey"] = "Asdasdsadsadsadsadsadsadsadasda-ASD";
configMap[? "authDomain"] = "adssadadsadsa.firebaseapp.com";
configMap[? "databaseURL"] = "asdsadsadsadsa.firebaseio.com";
configMap[? "projectId"] = "gmfirebase-asdsadadsa";
configMap[? "storageBucket"] = "adsadasdsadsadsa.com";
configMap[? "messagingSenderId"] = "1231321321";
configMap[? "appId"] = "1231131313213213";
firestore_web_init(configMap,true);

ds_map_destroy(configMap); //No more needed. Destroy..

At this stage, you’re completely SET! That’s it. You can now start using Firestore Extension.

DOCUMENTATION

firestore_web_init(dsMap configuration, True/False initialize?);
Description
: Initializes Firestore Web extension. It must be used before any other function. Setting the initialize argument as true, initializes the extension. BE NOTICED THAT, If you use any other Firebase Html5 extension of mine, still use this function, but only set initialize as true in ONLY ONE OF THEM.
Returns : N/A

firestore_web_init(myconfigMap,true);
cloud_storage_init(myConfigMap,false); //Because I already initiated Firebase in firestore.

firestore_web_debug_mode(True/False debugMode);
Description : Enables or disables debug mode for the extension. You can see what’s happening in the background in console log.
Returns : N/A

This function is removed from and no longer being used! All the are retrieved to Social Async Event now…
firestore_web_document_json();

Description : Backbone of the whole extension! When you fetch a document, you will set a callback script. Once the data is fetched, your script will be called. In your script, you must use firestore_web_document_json to get your document’s data.
Returns : A Json String (String)

This JSON string can be transferred to a DS_MAP via json_decode function.

firestore_web_listener_detach(String ListenerID);
Description
: When you start listening to a collection/document, you must set an ID, anything you want, like even “ILOVEYOU”. This function is used to STOP listening to that collection/document. Can be used for query listener, document listeners, collection listener; for any listener!
Returns : N/A

Network Settings

As you may know, Firestore can cache data so that you wouldn’t download them again. However, sometimes you do not wish to have such thing and force the SDK to fetch only from Server. This is all up to you, DEFAULT behaviour is recommended. Below functions control the network settings.

Using this functions does not effect previously requested operations. If you start listening to a collection, and then force SDK to only fetch local data: You will still listening to that collection. However, requesting another listening to operation will fail(because you started forcing SDK to avoid server connection)

firestore_web_default_network();
Description : Firestore SDK uses default behaviour for fetching data. If you’re a beginner, you should probably use this one.
Returns : N/A

firestore_web_enable_network();
Description : Forces Firestore SDK to fetch only from SERVERS, not from local cache.
Returns : N/A

firestore_web_disable_network();
Description : Forces SDK to fetch data only in the local cache. If you have previously fetched a data and it is saved to the local cache(by Firebase automatically), then you’ll be only fetching from these data pool.
Returns : N/A

Reading/Listening a Single Document

These two functions are responsible for getting a simple document.

PLEASE READ THESE TWO NOTES CAREFULLY!

Due to fact that Game Maker obfuscates scripts, you must ALWAYS name your callback scripts beginning with gmcallback_ For example gmcallback_MySongDataScript!
Also, when passing your callback scripts to functions, write them in STRING form (inside two “gmcallback_myFunctionLol”) > “”. For example; firestore_web_document_get(“my_songs/phil_collins”,”gmcallback_MySongDataScript”);
If you don’t want to callback any script, you can simply set the argument as “” firestore_web_document_get(“my_songs/bryan_adams”,””);

firestore_web_document_get(string documentLocation);
Description : Simply retrieves a document from Firestore database. Once it’s done, your callback script is called. If the argument[0] is 1, you can get the document’s data with firestore_web_document_json function and convert it to a ds_map with json_decode function.
Returns : A Social Async Event
“id” = Must be fireweb_async (Constant)
“category” = Category of the request. Must be “document_get”.
“location” = Location of the document (String)
“document_id” = The ID of the document (String)
“status” = fireweb_success, fireweb_fail or fireweb_error (Constant)
“data” = Documents’s data, which can be converted to a ds_map with json_decode function.

firestore_web_document_listen(String documentLocation, String listenerID);
Description : Starts listening to a document. This means that the document will be fetched and your callback script will be called. Anytime the selected document is changed, the Social Async Event will be recalled.
Returns : A Social Async Event
“id” = Must be fireweb_async (Constant)
“category” = Category of the request. Must be “document_listen”.
“source” = Source of the document. Since Firestore caches documents, this document may come from local cache or from the server. Either fireweb_change_server or fireweb_change_local. (Constant)
“location” = Location of the document. (String)
“document_id” = The ID of the document. (String)
“status” = fireweb_success, fireweb_fail or fireweb_error (Constant)
“data” = Documents’s data, which can be converted to a ds_map with json_decode function.

Writing Data

These functions are used for writing data to the database. Before sending data, you must form a JSON string. In Game Maker, you must create a ds_map, fill it with your way and convert it to a JSON string with json_encode.

var data = ds_map_create();
data[? "song"] = "Everything I do";
data[? "singer"] = "Bryan Adams";
data[? "someNumber"] = 9534;
var jsonStringOfMyMap = json_encode(data);
//We must clear the ds_map from memory now.
ds_map_destroy(data);

//Set a document
firestore_web_document_set("song_list/everything_i_do", jsonStringOfMyMap);

firestore_web_document_set(String documentLocation, Json Data);
Description
: Sets a document to the given location. The document will be populated with your ds_map(which is converted to a json string), please check the note above. A Social Async Event will be triggered to confirm whether the document is successfully set or not.
Returns : N/A
Callbacks : A Social Async Event
“id” = Must be fireweb_async (Constant)
“category” = Category of the request. Must be “document_set”.
“location” = Location of the document. (String)
“status” = fireweb_success or fireweb_error (Constant)

firestore_web_document_add(String collectionLocation, Json Data);
Description
: Adds a document to the given collection. A Social Async Event will be triggered to confirm whether the document is successfully set or not.
Returns : N/A
Callbacks : A Social Async Event
“id” = Must be fireweb_async (Constant)
“category” = Category of the request. Must be “document_add”.
“location” = Location of the document. (String)
“status” = fireweb_success or fireweb_error (Constant)

firestore_web_document_update(String documentLocation, Json Data);
Description : Updates an existing document with the data given. This will merge both existing and given data together. It’s the same as Merge function in Mobile Firestore. A Social Async Event will be triggered to confirm whether the document is successfully set or not.
Returns : N/A
Callbacks : A Social Async Event
“id” = Must be fireweb_async (Constant)
“category” = Category of the request. Must be “document_update”.
“location” = Location of the document. (String)
“status” = fireweb_success or fireweb_error (Constant)

firestore_web_document_delete(String documentLocation, Json Data);
Description : Deletes a document. BE NOTICED THAT, it does not return the document’s subcollections. You can still access to its subcollections by the same document location. If you need to delete a subcollection, you must do it manually or use firestore_web_query_delete function. A Social Async Event will be triggered to confirm whether the document is successfully set or not.
Returns : N/A
Callbacks : A Social Async Event
“id” = Must be fireweb_async (Constant)
“category” = Category of the request. Must be “document_delete”.
“location” = Location of the document. (String)
“status” = fireweb_success or fireweb_error (Constant)

Query Data

The chat example in this post queries a collection and listens to it. This is how I do chat example. For those who are not familiar with querying, it’s basically filtering the documents in a collection; getting only the things we want to have. Constant values used in querying are;

  • firestore_web_datatype_string : Indicates a String data type.
  • firestore_web_datatype_real : Indicates a Numberic data type.
  • firestore_web_order_ascending : Used for querying the results in ASCENDING order.
  • firestore_web_order_descending : Used for querying the results in DESCENDING order.
  • fireweb_change_add : If a document is got inside of the query scope.
  • fireweb_change_modify : If a document within the query scope is changed.
  • fireweb_change_remove : If a document within the query is removed, or got outside of the query.

firestore_web_query_create(collectionLocation);
Description : Starts a query on given collection.
Returns : N/A

firestore_web_query_equal_to(String Key, String Val, Const DataType);
Description : Adds a “==” mark to the query, meaning that only the documents that match with the given key=val are retrieved. DataType is either firestore_web_datatype_string or firestore_web_datatype_real.
Returns : N/A

firestore_web_query_greater_than_or_equal_to(String key, Double Value);
Description : Adds a “>=” mark to the query.
Returns : N/A

firestore_web_query_less_than_or_equal_to(String Key, Double Value);
Description : Adds a “<=” mark to the query.
Returns : N/A

firestore_web_query_greater_than(String Key, Double Value);
Description : Adds a “>” mark to the query.
Returns : N/A

firestore_web_query_less_than(String Key, Double Value);
Description : Adds a “<” mark to the query.
Returns : N/A

firestore_web_query_limit(Double Value);
Description : Limits the maximum amount of documents retrieved. This means that if your collection has 10 documents, but you limit the query to 2; you will only get 2 of them.
Returns : N/A

firestore_web_query_orderby(String Key);
Description : Orders the query by the key. Notice that if a document doesn’t have the key, that document will not be retrieved.
Returns : N/A

firestore_web_query_orderby_ext(String Key, Constant OrderDirection);
Description : Orders the query by the key, plus with direction. for OrderDirection, either use firestore_web_order_ascending or firestore_web_order_descending. Below example queries chat messages and orders them depending on their time.
Returns : N/A

firestore_web_query_create("chat_messages");
firestore_web_query_orderby_ext("time",firestore_web_order_descending); 

//This will only fetch documents that we haven't received yet!
firestore_web_query_execute_listen_changes("Hooo My Query");

firestore_web_execute_query_get(String queryName);
Description
: Simply executes the query and retrieves all the documents of the result. Documents retrieved will be in the Social Async Event. queryName is just for to remind you which file belongs to which query.
Returns : A Social Async Event
“id” = Must be fireweb_async (Constant)
“category” = Category of the request. Must be “query_get”.
“location” = queryName will be returned. (String)
“document_id” = The ID of the document. (String)
“status” = fireweb_success, fireweb_fail or fireweb_error (Constant)
“data” = Documents’s data, which can be converted to a ds_map with json_decode function.

firestore_web_query_execute_listen_all(String queryName, String dataListenerID);
Description : Starts listening to the query. Initially it’ll retrieve all the results one by one to Social Async Event. However, if anything changes in the query(Like a document changes in the query set), all the query results will be re-retrieved again.
Returns : A Social Async Event
“id” = Must be fireweb_async (Constant)
“category” = Category of the request. Must be “query_listen_all”.
“location” = queryName will be returned. (String)
“document_id” = The ID of the document. (String)
“status” = fireweb_success, fireweb_fail or fireweb_error (Constant)
“data” = Documents’s data, which can be converted to a ds_map with json_decode function.

firestore_web_query_execute_listen_all is usually an expensive function. I’ve made it in case you’d need anything.

firestore_web_query_execute_listen_changes(String queryName, String DataListenerID);
Personal Note
: I love this function!!
Description : Starts listening to the query. It will initially retrieve all the results one by one to Social Async Event function. Whenever a document within the query set is modified, removed(or it was in the query scope, now it’s not), or a new document is added, the Social Async Event will be triggered again(with only the document that’s catched to the listener)
Returns : A Social Async Event
“id” = Must be fireweb_async (Constant)
“category” = Category of the request. Must be “query_listen_changes”.
“location” = queryName will be returned. (String)
“document_id” = The ID of the document. (String)
“status” = fireweb_change_add, fireweb_change_modify or fireweb_change_remove depending on the change.
“data” = Documents’s data, which can be converted to a ds_map with json_decode function.

ONCE YOU’RE DONE LISTENING TO A QUERY/DOCUMENT/COLLECTION YOU MUST ALWAYS DETACH THE LISTENER With firestore_web_listener_detach

Below is a sample of returning document

GET THE EXTENSION

Open Source

Share

Marty

Truely speaking, I don't know what am I doing most of the time.

1 Response

  1. Joe Ellis says:

    Hello!

Leave a Reply