Generating Codes for Joining Challenges Using Game Data Service

Introduction

Like Kahoot sometimes you want your friends to join your game through an easy-to-remember code. Players enter a code and the system finds the game their friend has created and adds them to it.

This tutorial shows you how to meet this sort of use case in GameSparks:

Here's the steps we'll follow:

  1. Create a Data Type to save references to the generated codes, which Challenges they are linked to, and the time we created them (in miliseconds).
  2. Customize the CreateChallengeResponse Cloud Code script.
  3. Create an Event that uses the generated code to search for games and join them if a game exists.
  4. Set up a way to remove used codes so future games can use them.

Game Data Service? This tutorial assumes an understanding of the Game Data Service. We strongly recommend that you review the Game Data and the Data Explorer topics before you attempt to follow this tutorial.

Creating a Data Type and Adding Indexes

This step is easy - simply create a Data Type in the Game Data page and add the fields we want to use to query its data. We've called it challengeCodeDataType and we'll add two indexed fields - you'll see that we make references to these in Cloud Code.

How to Create Data Type? See the Game Data and Data Explorer pages for details on how to create and add indexes for a Data Type.

Customizing the CreateChallengeResponse Script

Our first task is to attach a customized Cloud Code script for the CreateChallengeResponse.

The reason we attach this code to the CreateChallengeResponse is because when the player has successfully submitted a CreateChallengeRequest, they receive a valid challengeInstanceId in the response. This is therefore a good place to verify that the player has a valid Challenge. We can then save the id, the generated code for the Challenge, and the timestamp in our Challenge and code Data Type that we called challengeCodeDataType.

1. In the portal, go to the Configurator>Cloud Code page.

2. In the Scripts panel, select Responses>CreateChallengeResponse. The Cloud Code editor opens.

3. Add the following code:

//We check if the response has a valid challengeInstanceId to carry on the sequence
if(Spark.getData().challengeInstanceId !== null){

    //Load data service
    var API = Spark.getGameDataService();

    //Get the challenge instance id generated
    var challengeID = Spark.getData().challengeInstanceId;


    //Generate random code of 000-000 format
    var code = randomCode();

    //If we did not get a valid code after 1000 attempts then force an error
    if(code === null){
        Spark.setScriptError("error", "NO VALID CODE CAN BE GENERATED")
    } else{
        //Create time stamp
        var date = new Date();

        //Create new entry in Data Type for players to search and join
        var entry = API.createItem("challengeCodeDataType", challengeID);
        var data = entry.getData();
        data.code = code;
        data.creationTime = date.getTime();

        //Persist entry
        var status = entry.persistor().persist().error();

        if(status){
            //Return the code to the player
            Spark.setScriptData("code", code);    
        } else{
            Spark.setScriptError("ERROR", status)
        }

    }

}

//Function to create number
function randomCode(){
    //Create a code, if this code is not valid (used already) keep trying for 1000 times, if none are valid, return null to be used to create an error message back
    for(var i = 0; i < 1000; i++){
        //Generate two 3 character long ints
        var firstThree = ("00" + Math.floor(Math.random() * 1000).toString()).slice(-3);
        var secondThree = ("00" + Math.floor(Math.random() * 1000).toString()).slice(-3);

        //Combine ints to form game search code
        var codeGen = firstThree + "-" + secondThree;

        //Create a condition comparing the code
        var condition = API.S("code").eq(codeGen);

        //Is there any other game using this code right now?
        var potentialMatch = API.queryItems("challengeCodeDataType", condition);

        //Was there an error trying to connect to data service
        if(potentialMatch.error() == null){
            //If not, break while loop by returning code
            if(potentialMatch.cursor().next() === null){
                return codeGen;
            }
            if(i >= 999){
                return null;
            }
        } else{
            Spark.setScriptError("ERROR", "Error retrieving data");
            return null;
        }

    }
}

Join Event

Because our players don't know the challengeInstanceId, the JoinChallengeRequest is not an immediate and viable way for players to join the Challenge. Instead, we need to create a custom Event that uses the generated code to:

1. Create an Event and add a single string Attribute and call it code.

2. Go to Configurator>Cloud Code and in the Scripts select the new Event under Events. The Cloud Code editor opens.

3. Add the following Cloud Code to the Event:

//Get code from input
var code = Spark.getData().code

//Load Data API
var API = Spark.getGameDataService();

//Create condition
var condition = API.S("code").eq(code);

//Attempt to get result
var resultOBJ = API.queryItems("challengeCodeCollection", condition);

//Check for errors
if(resultOBJ.error()){
    Spark.setScriptError("ERROR", "Could not retrieve data")
}else{
    //Get document
    var result = resultOBJ.cursor().next();
}

if(result){
    var challengeID = result.getId();

    //If game does not exist, return error
    if(challengeID == null){
        Spark.setScriptError("error", "NO GAME CAN BE FOUND");
    }
    //If game exists, join it and return response as scriptData
    else{
    //Create a join challenge request via code
    var joinRequest = new SparkRequests.JoinChallengeRequest();

    //Set the id of the challenge we wish to join
    joinRequest.challengeInstanceId = challengeID;

    //The sending happens here at .send()
    var joinResponse = joinRequest.Send()

    //If error is null or unidentified return the result of the request otherwise return the error
    if(joinResponse.error() == null){
        Spark.setScriptData("joined", joinResponse.joined);    
    } else{
        Spark.setScriptError("error", joinResponse.error)
    }
    }
}else{
    Spark.setScriptError("ERROR", "No game found");
}

Removing Old Entries for Re-Use

We want to remove old Challenge codes and make them available for re-use in new games. This can be done in a few ways:

You can combine both methods if you wish.

Method A

In this first method, we remove Challenge codes every day:

1. Go to Configurator>Cloud Code.

2. On the Scripts panel, select System>Every Day.

3. Add the following Cloud Code to the GS_DAILY script:

 //Create a date stamp
var date = new Date();
//Time stamp 24 hours ago by substracting the milliseconds equal to 24 hours
var timestamp = date.getTime() - 86400000;

//Load API
var API = Spark.getGameDataService();

//Create condition
var condition = API.N("creationTime").lt(timeStamp);

//Return results
var resultsOBJ = API.queryItems("challengeCodeDataType", condition);

if(resultsOBJ.error() !== null){
    //Get results
    var results = resultsOBJ.cursor();

    //Delete records
    while(results.hasNext()){
        results.next().delete();
    }

} else{
    Spark.setScriptError("ERROR", resultsOBJ.error())
}

Method B

In this second method, we remove the Challenge codes when a game is concluded:

1. Go to Configurator>Cloud Code.

2. On the Scripts panel, select Global Messages>ChallengeWonMessage or Global Messages>ChallengeLostMessage:

3. Open a second tab and select Global Messages>ChallengeDrawnMessage:

4. Add the following Cloud Code to both the Won/Lost and Drawn messages:

//Get challenge instance id
var challengeID = Spark.getData().challenge.challengeId;

//Load API
var API = Spark.getGameDataService();

//Attempt to find entry
var resultOBJ = API.getItem("challengeCodeDataType", challengeID);

//If error
if(resultOBJ.error()){
    Spark.setScriptError("ERROR", resultOBJ.error());
} else{
    //If entry is retrieved
    if(resultOBJ.document()){
        //Delete entry
        resultOBJ.document().delete();
    }
}

Removing Code for Expired or Withdrawn Challenges

If our Challenge is terminated for any reason before starting, we want to remove any reference of it and recycle the code generated for it:

This will ensure that if the Challenge is expired or withdrawn, we make the code available for re-use. It's important to leave the code in the Global version of the message because we only want to run it once. The Global version is the server version of the call that only executes once.

Did this page help you? Please enter your feedback below. For questions about using this part of the platform, please contact support here