Generating Codes for Joining Challenges
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:
- Create a system that generate codes for GameSparks Challenges.
- Give those codes back to the creator of the Challenge.
- Add codes to a runtime collection to keep track of codes and their corresponding Challenges.
- Remove codes for recycling.
Here's the steps we'll follow:
- Create a runtime collection to save references to the generated codes, which Challenges they are linked to, and the time we created them (in miliseconds).
- Customize the CreateChallengeResponse Cloud code script.
- Create an Event that uses the generated code to search for games and join them if a game exists.
- Set up a way to remove used codes so future games can use them.
Important! If you're working on a new game that was created after the Game Data Service was launched in January 2018, you won't be able to create new Mongo Runtime collections and the Cloud Code used in the tutorial will not work. For the alternative tutorial for new games using the Game Data Service, see Generating Codes for Joining Challenges Using Game Data Service.
Creating a Runtime Collection.
This step is easy - simply create a runtime collection. We've called it challengeCodeCollection and you'll see that we make references to it in Cloud Code.
How to Create Runtime Collections? See this tutorial for details on how to create a runtime collection.
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 challenge creation response 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 runtime collection.
1. 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 runtime collection where we save the code and reference to challenges
var challengeCollection = Spark.runtimeCollection("challengeCodeCollection");
//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{
var date = new Date();
//Create new entry in collection for players to search and join
challengeCollection.insert({"_id":{"$oid":challengeID},"code": code,"creationTime":date.getTime()})
//Return the code to the player
Spark.setScriptData("code", code);
}
}
//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;
//Is there any other game using this code right now?
var potentialMatch = challengeCollection.findOne({"code": codeGen});
//If not, break while loop by returning code
if(potentialMatch === null){
return codeGen;
}
if(i >= 999){
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:
- Find our game through searching the challengeCodeCollection runtime collection.
- Retrieve the challengeInstanceId.
- Invoke a JoinChallengeRequest for us through Cloud Code.
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
//Find game
var challengeOBJ = Spark.runtimeCollection("challengeCodeCollection").findOne({"code": code},{_id : 1});
var challengeID = challengeOBJ._id.$oid;
//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 happen 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)
}
}
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:
- Method A - By having a daily script remove any code older than, say, 24 hours:
- This could be after any length of time, 24 hours is used here as an example.
- A downside to this method is that your code can no longer be used to join that game after the set time has elapsed.
- Method B - By removing the code when a game is concluded:
- A downside to this method is that if the game is never concluded, the code is never re-used.
You can combine both methods if you wish.
Method A
In this example of this 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:
//Load our challenge and code collection
var challengeCollection = Spark.runtimeCollection("challengeCodeCollection");
//Create a date stamp
var date = new Date();
//Time stamp 24 hours ago by substracting the miliseconds equal to 24 hours
var timestamp = date.getTime() - 86400000;
//Remove any entry that has a creation timestamp of less than the timestamp we set above
//$lt is the 'less than' operator
challengeCollection.remove({"creationTime": { $lt: timeStamp }})
Method B
1. Go to Configurator > Cloud Code.
2. On the Scripts panel, select Global Messages > ChallengeWonMessage or GlobalMessage > ChallengeLostMessage:
- Do NOT add to both messages, because we want the code to run only once.
3. Open a second tab and select GlobalMessage>ChallengeDrawnMessage:
- Ensure it's the Global messages version to avoid duplication.
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 collection
var challengeCollection = Spark.runtimeCollection("challengeCodeCollection");
//Remove code entry for this challenge to be re-used
challengeCollection.remove({"_id":{"$oid":challengeID}})
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:
- Add the code for Method B above to each of the following messages and ensure you have the code in the GLOBAL Message versions:
- ChallengeExpiredMessage
- ChallengeWithdrawnMessage
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.