ruk·si

✨ GameSparks

Updated at 2016-09-08 00:37

GameSparks was a game backend-as-a-service. They have since started shutting down operations.

Register for the service. Create a game, record API key and API secret from the Overview tab.

Your game is in either Preview or Live stage.

  • Preview is for developing and testing the game with limited number of users.
  • Live is activated when the game launches.

Game configurations are managed with Snapshots. Create a snapshot when you are happy with the configuration. If you publish a snapshot, it will copy the configuration from it and move the game to the Live stage. This allows Preview stage of the game be active for development while players are in Live stage. You can also revert to old published snapshots if something goes wrong.

Download the GameSparks Unity tutorial project. Open Assets > GameSparks > TestUI > GameSparksTestUI.unity scene. Top bar will have a new option called GameSparks > Edit settings, fill API credentials in there. GameSparks Unity Setup

Your game can get a custom SDK. Has a custom C# class for custom Events and Leaderboards. Make sure to update the SDK if you change Event or Leaderboard structures.

Test Harness:

  • You can save a request or sequence of requests for later.
  • Green = Sent, Blue = Response, Orange = Asynchronous Response, Red = Closed Connection
  • Allows debugging Cloud Code on requests that have Cloud Code defined.
  • You can send multiple requests in single JSON using an array.

Naming

Short Codes:
        Attributes: ANACONDA_CASE
            Events: PascalCaseNoEventSuffix
      Leaderboards: PascalCaseNoLeaderboardSuffix
      Achievements: PascalCaseNoAchievementSuffix
     Virtual Goods: PascalCaseNoSuffix
        Everything: Make as short as possible while keeping it unique and
                    understandable e.g. GetPos, not GetPosition.

Names:
        Everything: Title Case Upper Words

Descriptions:
        Everything: Natural language with a period. Can have multiple sentences.

Authentication

Choose allowed auth options:

  • Device authentication, automatic login but device specific. But ChangeUserDetailsRequest allows setting display name for the player.
  • Username + Password (RegistrationRequest -> AuthenticationRequest)
  • Social Authentication, 12 providers e.g. Amazon, Facebook, Google+, Steam, Twitter. If player is already authenticated, social login will link the account if it is not linked to something else, otherwise it will relogin with the existing social login account.
RegistrationRequest and DeviceAuthenticationRequest always create new accounts.
All *SERVICE*ConnectRequests have the option to link to current player.

Cloud Code

Events are the way to communicate with the platform on your own rules. Events are custom data structures that you want to pass into the platform via the LogEventRequest and LogChallengeEventRequest API calls.

Cloud Code allows binding JavaScript functionality to the platform. You can run the code when an Event, Request, or Response is received or sent. LogEventRequest and LogChallengeEventRequest scripts are global that will be executed for these request types before a specific script is called for each Event. This allows you to add a common functionality to all Events.

  • Configurator > Events > (+) to create 2 events:
    • First event:
      • SetPos, Set Position, Sets player position.
      • position, POS, JSON, {"x":0,"y":0,"z":0}, Used In Script
    • Second event:
      • GetPos, Get Position, Returns player position.
  • Configurator > Cloud Code > Events > Set Position
var POSV = Spark.getData().POS;
Spark.getPlayer().setScriptData("POSVAR", POSV);
  • Configurator > Cloud Code > Events > Get Position
var POS = Spark.getPlayer().getScriptData("POSVAR");
Spark.setScriptData("POS", POS);
  • Test Harness
    1. RegistrationRequest and fill in the details.
    2. LogEvent > SetPos and fill in the values.
    3. Player > AccountDetailsRequest
    4. LogEvent > GetPos

Other interesting Cloud Code Event examples:

  • "Every Day, Hour, Minute"
  • "File Delivered - This script is executed when a file is delivered via SFTP to the GameSparks platform. SFTP access to the GameSparks platform is available on request. Please raise a support ticket to request this."

Saving Example

Saving in Unity:

new GameSparks.Api.Requests.LogEventRequest()
    .SetEventKey("SavePlayer")
    .SetEventAttribute("XP", 123456)
    .SetEventAttribute("POS", playerPosition.ToString())
    .SetEventAttribute("GOLD", 100)
    .Send((response) => {
        if (!response.HasErrors) {
            Debug.Log("Player Saved To GameSparks...");
        } else {
            Debug.Log("Error Saving Player Data...");
        }
    });

Saving in cloud code bound to SavePlayer event:

var playerDataList = Spark.runtimeCollection("playerData");
var playerID = Spark.getPlayer().getPlayerId();
var playerExperiance = Spark.getData().XP;
var playerGold = Spark.getData().GOLD;
var playerPos = Spark.getData().POS;
var currentPlayer = {
  "playerID": playerID,
  "playerXP": playerExperiance,
  "playerGold": playerGold,
  "playerPos": playerPos
};
playerDataList.update(
  {
    "playerID": playerID
  },
  {
    "$set": currentPlayer
  },
  true, // Create the document if it does not exist.
  false // This query will only affect a single object.
);

Loading in cloud code bound to LoadPlayer event:

var playerData = Spark.runtimeCollection("playerData");
var currentPlayer = playerData.findOne({
    "playerID": Spark.getPlayer().getPlayerId()
});
Spark.setScriptData("playerData", currentPlayer);

Loading in Unity:

new GameSparks.Api.Requests.LogEventRequest()
    .SetEventKey("LOAD_PLAYER")
    .Send((response) => {
        if (!response.HasErrors) {
            Debug.Log("Received Player Data From GameSparks...");
            GSData data = response.ScriptData.GetGSData("player_Data");
            print("Player ID: " + data.GetString("playerID"));
            print("Player XP: " + data.GetString("playerXP"));
            print("Player Gold: " + data.GetString("playerGold"));
            print("Player Pos: " + data.GetString("playerPos"));
        } else {
            Debug.Log("Error Loading Player Data...");
        }
    });

You can also test this with test harness if you define the events and attributes.

Note that if you post partial save e.g. missing GOLD, it will be set to the default value but if the default value is not defined, it is required.

Leaderboards

Getting started example:

  • Configurator > Events > (+) to create an event:
    • LeaderScore, Leaderboard Score, Adds the score to the leaderboard.
    • Score, SCORE, Number, -, Maximum
    • Maximum because we want to track the highest score posted for your player.
  • Configurator > Leaderboards > (+) to create an leaderboard:
    • HighScore, High Score, List of players with the highest score.
    • Rest are default except add a running total and select Leaderboard Score.
  • Test Harness > Authentication > AuthenticationRequest
    • Add userName and password that you registered before or register first.
    • LogEvent > LeaderScore and fill in some score.
    • Leaderboards > LeaderboardDataRequest, fill entryCount and leaderboardShortCode and remove challengeInstanceId.

You can use Running Totals to process events using the GameSparks scoring and ranking systems. Used with events to create leaderboards. Player or team based, minimum, max, sum etc.

Achievements

Achievements are cross-platform. Define once, use on all systems. Each achievement can have a reward.

Achievements can be triggered by leaderboard or in cloud code. You can list player's achievements with AccountDetailsRequest.

Getting started example:

  • Configurator > Achievements > (+) to create an achievement:
    • YouAreAwesome, You Are Awesome, Awarded when player becomes awesome.
    • Currencies = 1, 1, 3, 1, 1
  • Configurator > Events > (+) to create an event:
    • BecomeAwesome, Become Awesome, Player becomes awesome!
  • Configurator > Cloud Code > Events > Become Awesome
var player = Spark.getPlayer();
player.addAchievement("YouAreAwesome");
  • Test Harness
    • Authentication > AuthenticationRequest and fill in userName and password that you registered before or register first.
    • Player > AccountDetailsRequest and notice that player doesn't have any currency.
    • LogEvent > BecomeAwesome and notice that you get achievement message.
    • Player > AccountDetailsRequest and notice that player gained currency.

Messages

Messages are in-game messages to players or push notifications. Also allows creating chat systems between players. You can basically send any messages from cloud code to clients on top of the default messages.

For online clients, messages are sent using WebSockets. For offline clients, messages are sent using push notifications if allowed.

Messages are sent asynchronously so you need to listen to them yourself. This is done by message listeners.

NewHighScoreMessage example:

void Awake() {
    GameSparks.Api.Messages.NewHighScoreMessage.Listener += HighScoreMessageHandler;
}
void HighScoreMessageHandler(GameSparks.Api.Messages.NewHighScoreMessage _message) {
    Debug.Log("NEW HIGH SCORE \n " + _message.LeaderboardName);
}

AchievementEarnedMessage example:

void Awake() {
    GameSparks.Api.Messages.AchievementEarnedMessage.Listener += AchievementMessageHandler;
}
void AchievementMessageHandler(GameSparks.Api.Messages.AchievementEarnedMessage _message) {
    Debug.Log("AWARDED ACHIEVEMENT \n " + _message.AchievementName);
}

Virtual Goods

Virtual Good is any persistent in-game asset. They can be awarded, accumulated, traded, converted or bought. Items that give XP, in-game currency, customization etc.

You can change how much currencies players get when they sign in. Configurator > Overview > Top box edit

Getting started example:

  • Configurator > Virtual Goods > (+)
    • BoosterPack, Booster Pack, Small increase to energy, 1
  • Test Harness
    • Authentication > AuthenticationRequest and login.
    • Player > AccountDetailsRequest shows how much currencies you have.
    • Store > BuyVirtualGoodsRequest fill in 1, 1 and BoosterPack.
    • Player > AccountDetailsRequest now shows currencies and good you have.

Increase player currency in cloud code:

var newCash = Spark.getData().Cash;
Spark.getPlayer().credit1(newCash);

See currency and goods in Unity:

new GameSparks.Api.Requests
    .AccountDetailsRequest()
    .Send((response) => {
        if (!response.HasErrors) {
            Debug.Log("Account Details Found...");
            string playerName = response.DisplayName;
            int cashAvailable = (int) response.Currency1;
            int packCount = (int) response.VirtualGoods.GetNumber("BoosterPack");
        } else {
            Debug.Log("Error Retrieving Account Details...");
        }
    });

Buying virtual goods in Unity:

new GameSparks.Api.Requests
    .BuyVirtualGoodsRequest()
    .SetCurrencyType(1)
    .SetQuantity(1)
    .SetShortCode("BoosterPack")
    .Send((response) => {
        if (!response.HasErrors) {
            Debug.Log("Virtual Goods Bought Successfully...");
            UpdatePlayerDetails();
        } else {
          Debug.Log("Error Buying Virtual Goods...");
        }
});

Consuming virtual goods in Unity:

new GameSparks.Api.Requests
    .ConsumeVirtualGoodRequest()
    .SetQuantity(1)
    .SetShortCode("BoosterPack")
    .Send((response) => {
        if (!response.HasErrors) {
            Debug.Log("Virtual Goods Consumed Successfully...");
            UpdatePlayerDetails();
        } else {
            Debug.Log("Error Consuming Virtual Goods...");
        }
    });

Buying Virtual Goods with Real Money

Google Play:

To make purchases using Google Play, you first need to set up your Virtual Good as a product in the Google Play Developer Console. Then you add the Google Product ID to the Virtual Good. Remember to add Configurator > Integrations > Google Play Public Key.

After making the purchase in the client, Google Play will invoke your response Intent. Purchaising an Item

String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
// Then you send GooglePlayBuyGoodsRequest:
{
  "@class": ".GooglePlayBuyGoodsRequest",
  "signature": "dataSignature",
  "signedData": "purchaseData"
}

And then player profile will have the virtual good.

iOS App Store:

First you need to set up your Virtual Good in iTunes Connect. Then you add the iOS Product ID to the Virtual Good. Finally you send IOSBuyGoodsRequest.

Downloadables

Downloadables are binary data that players can download. Like new levels or other assets.

Data Persistence

There are 4 main ways to store custom data on GameSparks platform:

Player Data

Small amounts of player-related data can be stored on the Player collection in either scriptData or privateData. Stored closely with currencies and virtual goods. Allows strings, numbers and JSON. Data stays as long as long the player exists.

// Script Data
Spark.getPlayer().setScriptData("myCustomData", { "subKey" : 1 } );
var myCustomData = Spark.getPlayer().getScriptData(“myCustomData”);
// And then it'll be available with AccountDetailsRequest and other API
// queries so it's great for avatar and other public info.

// Private Data
Spark.getPlayer().setPrivateData("myPrivateData", { "secretStuff" : 1 } );
var myPrivateData = Spark.getPlayer().getPrivateData(“myPrivateData”);
// None of the API requests return private data, you will only read
// these through cloud code.

Collections

Larger structured data should be stored in either metadata or runtime collections. Metadata collections are read-only at runtime and require new Preview -> Publish -> Live rotation. Runtime collections, by contrast, can be altered at anytime. Both are accessed with cloud code and use MongoDB backend.

var document = { "gameState": { "gameType": "deathMatch" } };
Spark.runtimeCollection("largeData").save(document);

var query = { "playerId":"12345" };
var data = Spark.runtimeCollection("largeData").findOne(query);

You can also index the collections. You should place the calls to ensureIndex for your collections in the System > Game Published cloud code, to ensure they are only called once for each collection.

Spark.runtimeCollection("largeData").ensureIndex({"gameState.gameType": 1});

Only push changes made to the collection. Try to avoid sending the whole document as collections have an update action.

Spark.runtimeCollection("largeData").update(
  { "playerId": Spark.getPlayer().getPlayerId() },
  { "$set": { "myKey": "myValue" } },
  true, // Create the document if it does not exist.
  false // This query will only affect a single object.
);

Never store binary data in collections.

Binary Assets

Binary assets and data should be stored using Downloadables and Uploadables.

For uploads, first ask for an upload URL from GameSparks. The upload will return uploadId which you can use to store upload metadata in collections. The uploadId and GetUploadedRequest can them be used to download the data by this or other clients.

Downloadables are for binary data common to all of your player like levels, asset packs, etc.

Transient Data

Transient data that requires fast operations can be stored in Redis. Redis is a lot faster than collections. MongoDB is better for queryable and large data while Redis is better for simple data structures like key-values, lists, hash tables and sets. You can only query Redis data based on ID. Redis also works in-memory so storing large amounts of data in it will be a challenge.

Spark.getRedis().sadd("MySet", 1);
Spark.getRedis().sadd("MySet", 2);
Spark.getRedis().sadd("MySet", 1);

Social Features

  • Link social profile: you get more info from the player if allowed.
  • Invites: allows listing player friends that can be invited.
  • Matchmaking: allows challenging friends to combat each other and chat.
  • Chatting: players can send messages between friends.
  • Rankings: leaderboards have global, segmented and friend ranking.

Challenges allow you to set up multiplayer game mechanics to encourage players to get their friends to play with each other.

Segments allow you to create partitions for different users and apply various rules to different areas of the platform. Segments add an extra dimension of customization to the players behavior e.g. segmentation by country or language to provide translated notifications.

Teams

Teams can be:

  • Social: all members of the team are considered friends.
  • Extended Social: all members of ANY team of this type are considered friends.

You can have a number of different team types with different rules and these rules are respected when using the team-based API Methods.

Teams can get team achievements.

Experiments

Manage > Experiments allow trying out different configuration for different players. Change names, virtual good costs, achievement, rewards, etc.

Debugging

You can find error logs in Manage > Script Log

Real-Time

GameSparks has real-time cloud code support for multiplayer games. Cloud Code > Realtime Scripts. Note that other scripts use Spark API while real-time functionality uses [https://docs.gamesparks.com/api-documentation/realtime-api/rtsession.html](RTSession API).

Note that code changes are not instant. When real-time cloud code is executed, it will be cached. It will be cached for 5 minutes after the last player disconnects.

// This function will be triggered when any packet with the
// op-code 100 reaches the server.
RTSession.onPacket(100, function(packet){ });
// All important game validation should be in these to prevent cheating.

You can call Spark API with RTSession.newRequest().

GameSparks has a good all-in-one multiplayer tutorials:

Scalability

Everything is automatically scaled. Forum post says they have tested with 100,000 concurrently connected players, requires further testing. 6,000 API calls per second was working as intended.

Sources