Introduction

Feature Parity
Leaderboards
Teams
Currency
Virtual Goods
Authentication
Push Notifications
Chat
Matchmaking
Email
Permissions & Groups
Cloud-Code Configurable
Database Access
Analytics
Player Management
Leaderboards

AccelByte is a bespoke backend service powering a number of large multiplayer games. While AccelByte has good feature parity with GameSparks it is a comprehensive game backend platform that includes web and PC launcher experiences that can be customized and white-labeled for you to have your own Battle.net. AccelByte also provides high-quality analytics, monetization, debugging and multiplayer features, along with a rich set of in-portal administration dashboards to empower clients to run their game-as-a-service businesses.

As we will see throughout these topics, they provide many of the same features as GameSparks and those features are configurable through an online portal just like GameSparks without any need for interaction from AccelByte. You can contact AccelByte to request a demo environment here and to start onboarding onto their platform.

In this series of topics we will be covering the Unity3D SDK for AccelByte. It is available for download here and comes with instructions for set up and integration into your existing project. This series starts with the Authentication Basics below. You can use the guide there to see how to set up a new AccelByte project and authenticate players.

Authentication Basics

There are various different authentication requests available with GameSparks but some of the most commonly used requests are the basic DeviceAuthenticationRequest, RegistrationRequest and AuthenticationRequest (username/password). We will be focusing on these basic authentication requests in this topic.

This topic doesn't cover 3rd party and social authentication. There is another topic here which covers that.

Device Authentication
Device-auth is most often used as anonymous authentication where the player is not required to submit any credentials before gaining access to the game.

With GameSparks, this request still uses device and OS settings for authentication so the device can be identified, so there is some identification needed but they are provided programmatically.

You can think of that while migrating device-auth calls. If the platform does not have an alternative, but does have username & password, you can use some hashed device details like the appId or mac address as a unique Id instead.

Registration
Registration allows a player to be created with a username and password for their account so they can log in on multiple devices.

Authentication Request
The basic auth-request just uses username & password. It requires registration before the details are valid, unlike device authentication which automatically signs you in and creates the player’s account.

Note - Not all destination platforms will have the same kinds of authentication requests as GameSparks has so we will try to cover these 3 forms of auth as best we can for each platform.

For frontend examples and code-snippets, we will also be focusing on Unity C# examples as it is the most popular engine used with GameSparks.

Accelbyte has a similar flow to GameSparks in that a user must first be registered before you can log the user in.

Registration
New users need more details than you would be used to with GameSparks. New users also need their email, country and date of birth. This is detailed here and in their Unity Sample guide. Although these details are needed for registration, you could add dummy data in here in order to migrate the code.

Requests from the client SDK are similar to the GameSparks with a list of parameters being passed into an SDK function, which also raises a callback when the login response comes back from Accelbyte.

private static void OnRegister(Result<UserData> result)
{
    if (result.IsError)
    {
        Debug.Log("Register failed:", result.Error.Message);
    }
    else
    {
        Debug.Log("Register successful.");
    }
}
public static void Main(string[] args)
{
    var user = AccelBytePlugin.GetUser();
    string email = "useremail@example.com";
    string username = "JohnDoe";
    string password = "password";
    string displayName = "displayed";
    string country = "US";
    string dateOfBirth = "1995-12-30";
    user.Registerv2(email, username, password, displayName, OnRegister, country, dateOfBirth);
}

Authentication
Once a user has been registered, it is very easy to log them in using their username and password. Their docs here show how to create a new user object and log them in with only a couple of lines of code in Unity.

User user = AccelBytePlugin.GetUser(); user.LoginWithUserName(email, password, (Result result) => { });

Device Authentication
Accelbyte also has a very simple replacement for device-authentication. These accounts are called “headless” accounts by Accelbyte as they have no username until the username has been set.

User user = AccelBytePlugin.GetUser();
user.LoginWithDeviceId((Result result)=>{ });

Authentication (3rd Party)

In this topic we are going to cover some of the more popular forms of 3rd party authentication. We will be focusing on how to migrate or replicate GameSparks requests specifically, SignInWithAppleConnectRequest, GooglePlayConnectRequest and FacebookConnectRequest.

We assume that developers using these forms of authentication already have developer accounts and apps setup for each of these platforms, but we have added links on how to set those up where we can.

There are of course quite a number of other GameSparks authentication requests but we will focus on these 3 for this topic.

Some of these alternative platforms have their own versions of the above authentication requests and some of them have other options available out-of-the-box. For this topic we will also touch on those different options, but our focus will be on replicating the three requests mentioned above.

For GameSparks, the flow for authentication is as follows:

  1. We get the player/client token for the platform using the client SDK Like the Facebook Unity plugin which can get you a Facebook access token.
  2. We send that token or code to the correct GameSparks request
  3. Under-the-hood, GameSparks sends that token, along with other details configured in the Integrations section of the portal, to a validation endpoint associated with the platform.
  4. The platform API validates the token and returns any relevant information. The important information we need is some way to identify the player by Id from that token.
  5. When we have that identity, we can look for that player and authenticate them locally.

This is the flow we will try to replicate where possible. None of this is too complicated, we really just need to implement the right server-to-server calls to validate the token once we get it.

Note - You could do all this client-side, but this would expose your game’s apiKey, secret or other hidden credentials. This is why we always make this request server-to-server.

AccelByte has a number of different options for 3rd party authentication, however they do not have an option for GooglePlay or Apple Sign-in.

They do have options for IOS and Google Account login, but these are not the same as the ones used by GameSparks.

The set up and authentication flow for AccelByte and the Unity SDK is very different to GameSparks. There is no integration section of the AccelByte portal where you can fill out the 3rd party integration details. Instead this is done via REST (example before for Facebook authentication).

If you wish to explore this auth flow for yourself you can check out the Facebook example below. Each of the authentication routes provided by AccelByte follow the same with minor modifications to the setup parameters and tokens/secrets required for authentication.

Facebook Authentication
Accelbyte does not offer the same form of authentication as GameSparks does using the FacebookConnectRequest and the Facebook access-token taken from the FB SDK. Instead, authentication is done through a browser redirect in order to get an access code from AccelByte and log your player in. Below will go through this process in brief so you can get a better understanding about the flow.

Setup Platform Integration
With GameSparks, you can set the details of your 3rd party platform directly through the Admin portal. With AccelByte you can also do this for some platforms, but not Facebook unfortunately. Instead, this is done through a REST API.

We will explain the flow of adding a third-party platform to AccelByte. This is not specific to Facebook. The same flow applies to other platforms with minor variations:

  1. First we need an Admin Access-Token. We can get this using the TokenGrant endpoint. We have already covered this topic in Chat & Messaging Topic
  2. Using this Access-Token, we need to create a Bearer-Token. We need to call the ThirdPartyPlatformCredential endpoint with this Bearer-Token.

We also need to supply additional platform details for Facebook such as ClientId, App Id, and Secret.

Once the platform details have been set you can make use of Facebook authentication with AccelByte.

The following flow requires you to reach out to AccelByte over REST from the client. It will also require you to set up an in-app browser call in order to get a valid auth-code from AccelByte for your user.

  1. Open a new browser with the following URL. https://www.facebook.com/v11.0/dialog/oauth?client_id=$YourFacebookClientID&redirect_uri=https://dev.accelbyte.io/
  2. You will be redirected to the portal
  3. This URL will contain a code, which you will need to copy. The URL should look something like https://dev.accelbyte.io/?code=AQBiuaUU7GSUgX.....
  4. Get a Grant-Token using the endpoint here. platform Id = “facebook” platform Token = The code from the redirect URL client_id = Facebook client id device_id = any unique id of your preference
  5. Once this request have been executed you can log-in through the client using the platform token you got from the redirect URL

Your user is now logged into the backend and you can log them into the client using the following code…

public void LoginUser()
{
    abUser.LoginWithOtherPlatform(PlatformType.Facebook, platformToken, LoginCallback);
}

Other Authentication Mechanisms There are a number of overlaps with GameSparks authentication mechanisms for AccelByte.

AccelByte for example has Twitch, PSN, Xbox, Discord and Twitter authentication, as does GameSparks. Since we are only focusing on Apple, GooglePlay and Facebook for this topic we aren't going to cover them, but you can see them listed here. The auth setup and flow is the same as above in the Facebook example.

Leaderboard Basics

There are a variety of different Leaderboard types and configurations available from GameSparks. However, not all these options will be available on other platforms. This topic will cover basic Leaderboard migrations involving setting up a leaderboard, posting scores, returning Leaderboard data and player specific Leaderboard data.

This topic will not cover partitioned Leaderboards. As partitioned Leaderboards are widely used by GameSparks developers, these are covered in their own topic here. It will also not cover all of the functionality of GameSparks leaderboards like social notifications, high scores, top X, etc., as these features are not available to all other platforms. Where available, we will cover some of these topics.

In comparison to GameSparks, Accelbyte provides a basic set of Leaderboard features, but they have daily and weekly leaderboards out-of-the-box, as well as timed Leaderboards for events or seasons.

The biggest difference between the two platforms is that AccelByte does not have features for providing social friends, team IDs, team types with their Leaderboards.

There is also no API which would allow you to add supplemental data to their Leaderboards for posting additional player data along with scores, for example avatarId, level, displayName, etc.

Leaderboards Setup
In Gamesparks, we start by creating a Leaderboard configuration like shortCode, name, description, high-score notifications and a few others. Similarly, we must add a new Leaderboard configuration in AccelByte through either admin portal. You can also do this through their REST API, but we will not cover that in this topic.

In order to create a new Leaderboard, we have to add a new Stat before creating the Leaderboard. We then associate that Stat with the Leaderboard we will create in a moment.

The Stat is like Running Totals in GameSparks. GameSparks Leaderboards consume data from a Running Total while AccelByte takes data from their statistics service. The Running Total in Gamesparks could be an event/attribute that you have created. In AccelByte, the statistics service is called by the game client to update stats (i.e., post a score) which in turn publishes that stat to the Leaderboard and updates it.

Creating New Stats
Under the Statistics category, you can find Configurations options. Here, you can add New Configuration.

As you can see, you can specify whether to increment or decrement, set minimum and maximum values, etc. The Set By field allows you to specify who has permission to set the value (Server or client if you only allow the server to post scores).

There are other fields you can see from the image, but we are only concerned with those mentioned above for this topic. Important

When creating a new Stat for use with a player Leaderboard you need to set the Set As Global option to false. Global stats are not player-specific, so they are not suitable for Leaderboards.

Creating a new Leaderboard
We can create a Leaderboard easily in the portal as shown below.

Once you click Create Leaderboard option above, a small window will pop-up to configure the Leaderboard with all the required fields.

In contrast to the Leaderboards in Gamesparks, you can specify the date this Leaderboard is going to become active. You can also set a reset time for other types of Leaderboards like Daily, Weekly, Monthly and Seasonal Leaderboards.

AccelByte’s resetting Leaderboards are split into their own Leaderboards, which means you will have a separate version for the main (non-resetting Leaderboard) and another for the daily, weekly, etc.

Note that a Leaderboard will not be active until its start time and it will consider the latest Stat values once they are started.

Posting Stats/Scores
In AccelByte we do not post scores directly to the Leaderboard API. Instead, we use the statistics service API to increment that Stat which is tied to the Leaderboard.

If the player does not have a Stat set up, the statistics service API will automatically create one for them when they call the IncrementUserStatItems() function.

abStatistic = AccelBytePlugin.GetStatistic();
abStatistic.IncrementUserStatItems(statItemOperationResult, OnIncrementUserStatItems);

This will initiate a player Stat with the score given or based on the default configuration. Next time the score is posted, it will start incrementing the desired value.

We can see the Leaderboard data in the admin portal by going to the Leaderboard category and clicking on the Action Menu to select the View option.

After following the above steps, we can see the leaderboard data as shown below with Rank, Display Name, Username, points.

Updating Stats/Scores
You can update the above player Stat using the below API call.

abStatistic.UpdateUserStatItems(statItemOperationResult, OnUpdateUserStatItem);

This will overwrite the Stat value which is previously stored and triggers the Leaderboard to be updated.

Returning Leaderboard Data
Using Gamesparks we can retrieve a Leaderboard based on multiple options using the LeaderboardDataRequest. However, with AccelByte, we can only get Leaderboard entry data with very basic details like offset and limit parameters.

Below is an example of how you can return the Top 10 entries of a given Leaderboard.

abLeaderboard.GetRankings(leaderboardCode, LeaderboardTimeFrame.ALL_TIME, 0, 9, OnGetTopTenRanking);

The ALL_TIME enumerator shown in the function above indicates that we want to request these entries from the ALL_TIME Leaderboards, which would be the equivalent of the basic GameSparks non-resetting Leaderboards. The other options available are MONTH, SEASON and TODAY.

The GetRankings function would return the data from which each player leaderboard data can be retrieved.

Returning Player Leaderboard Entry
We can retrieve the player Leaderboard data, much like in Gamesparks, using below code. In contrast to the GameSparks LeaderboardsEntriesRequest, getting player entries with AccelByte is very simple. We have only to supply the userId, along with the leaderboardCode.

abLeaderboard.GetUserRanking(AccelByteManager.Instance.AuthLogic.GetUserData().userId, leaderboardCode ,OnGetMyRanking);

The GetUserRanking function returns the data from which certain player leaderboard data can be retrieved.

Leaderboard Resetting
AccelByte already has resetting Leaderboards for daily, weekly and monthly resets out-of-the-box. You can see more on that feature here.

Leaderboard Partitions

A widely used feature of GameSparks is partitioned Leaderboards. These types of Leaderboards use the same APIs for posting and listing Leaderboard scores as regular Leaderboards, but they can be subdivided into smaller Leaderboards using a partition key.

For example, a world-wide Leaderboard on GameSparks with the short code “Arena” could be subdivided into countries by posting the partition key as the country code. In this case, all entries will go into the world-wide “Arena” Leaderboard, but the scores are also automatically posted to separate Leaderboards for the player’s country, for example “Arena.us”.

For alternative platforms this is not always the case, so in this topic we will cover how to use the native APIs to achieve partitioned Leaderboards, and if the platform does not have an alternative to partitioned Leaderboards, how you can replicate the same functionality so you can migrate your existing GameSparks partitioned Leaderboards.

Creating, posting and listing leaderboard data is not covered in this for each case as these points are covered in the topic on Leaderboard Basics available here.

Unfortunately, AccelByte does not provide a solution for partitioned Leaderboards out-of-the-box. You will need to recreate that functionality yourself. The simple alternative to this would be to create multiple leaderboards connected to the same stat.

For example, if you have a main “global” leaderboard called “Arena” you can create one leaderboard with that name and then a leaderboard for each of your country codes, i.e., “Arena.us”.

Because you are not actually posting to the leaderboard but instead, the leaderboard gets updated when the stat changes, both leaderboards will get updated automatically so there is no need for custom code.

This does mean that you need to manually set up each leaderboard with each key name, which could take some time, but a migration using AccelByte leaderboards is possible without custom backend code or client code.

Virtual Currency

Virtual Currency is a basic feature that most alternative platforms provide. The minimum requirement is the ability to create a key/value pair, tied to the player’s account which can be credited and debited to change the player’s balance.

Virtual Currency is usually associated with other features like Achievements and Virtual Goods. Those features should allow Virtual Currency to be granted or consumed on behalf of the player. They should also be accessible through some sort of API so that the developer can choose how they are credited and debited, such as GameSparks’s SparkVirtualCurrency API.

Some of the platforms discussed below include these features, but there are always differences between their implementation and GameSparks’. Keep in mind that crediting/debiting the player directly from the client is not recommended for any platform, so, in some places you may need to use the platform's Cloud-Code alternative to credit and debit the player.

GameSparks has two versions of Virtual Currency. One is the old version of currency which is the numbered currency values that come with every new player account.

The others are the currencies you can set up and name yourself from the configurator.

The only other feature related to Virtual Currency which GameSparks has, is the ability to automatically set a sign-up bonus for each currency. This isn't available for all alternative platforms, but we will show how that can be achieved where possible.

Virtual Currency in AccelByte has similar workings to GameSparks where we use it to purchase or deliver Virtual Goods, however, with AccelBytes’s Virtual Goods we have to set up the Virtual Currencies before we can use them.

You can find two types of currencies: Real and Virtual. These types of currencies are not like the Currency Type in GameSparks but instead are more beneficial in setting up Items (Virtual Goods) in AccelByte. In fact, they are defined to specify what kind of currency we can accept for payments. Real currency is like any other actual currency while Virtual is a virtual currency in the game. With AccelByte you must first create a wallet specifically for each Virtual Currency in order to add a value to it. This differs from GameSparks where we create currency automatically when the player account is created, and the sign-up bonus can credit the player freely from there.

Creating New Virtual Currencies
From the admin portal we can find Currencies under the E-Commerce category. If we click on that option, it will bring up a page where we can create different currencies. Click on Add option and then a small window will pop-up. We can select currency type, currency code and symbol and click Add to configure the currency. We can see our listed created currencies as shown below.

Player Wallet
When currencies are added to the player’s account they appear in their player wallet.

The wallet is automatically created when the first currencies are credited to the player, however, the wallet cannot be created on its own, it is created automatically.

However, the AccelByte Unity SDK is designed to prevent hacking by players who would reverse engineer those APIs to give themselves an advantage, like crediting themselves currency or granting themselves items. This means we cannot update the player’s currency and therefore their wallet from Unity. So how do we do that?

There is an example of how you can achieve this for testing in our topic on Cloud-Code here, however, the longer-term solution is to create your own backend that can utilize the AccelByte Golang or JS SDKs or work with AccelByte to create your own custom microservice.

Sign-Up Bonus
Accelbyte does not provide a signup bonus like in GameSparks. However, there is an alternative solution through Augment. We have already shown off crediting the player wallet with the desired amount. If you would like to trigger this signup bonus when the player is created then we need to choose one of the Kafka triggers. You can find more info regarding player wallet in this here.

Virtual Goods

GameSparks Virtual Goods are a pretty simple feature. In essence they are just some configuration data which defines the code, name, description and the cost of the Virtual Good in some preconfigured Virtual Currency related to the platform.

GameSparks does have a number of other features such as bundles and tags. These additional features are available on some platforms but not all of them, so we won't discuss how to reproduce those. Instead we are going to focus on the following requirements for migration.

  1. We need to be able to create a flexible definition that contains the code, name and description.
  2. We need to be able to define a cost for the item.
  3. We need an API which can grant this item to the player. Ideally the API should be able to debit the cost of the item when granting it.
  4. If the API cannot debit the cost of the item, we need to be able to debit the cost manually ourselves.

GameSparks Virtual Goods also include 3rd party integrations like Google or Apple Products which we can define with the Virtual Good and let GameSparks validate purchases and grant these items to the player from the backend. We aren't going to cover that in this basic example, but there is another example here where we discuss reproducing those features.

In AccelByte, Virtual Goods are known as Items. When compared to GameSparks, AccelByte has more configuration options on these Items which might be useful to you.

For example, while GameSparks allows you to create Virtual Goods as goods or consumable coin-packs, with AccelByte, you can configure Items to represent:

There are more details on how to set up each one of these types here, so we won't go into specifics on each item-type.

Virtual Goods Setup
Unlike GameSparks, creating a Virtual Good requires a few extra steps before we can begin.

First you need to make sure you have some Virtual Currencies setup. We cover that flow in another topic here.

You will also need to create a store. This store is where we add Virtual Goods, currency-packs, etc.

We can create a store in the Admin portal by going to the Stores and clicking Create right beside Draft stores as shown below.

Next, we need to create categories to place certain Virtual Goods or Items in a meaningful category based on type of item. You can call these categories whatever you want, but it might be handy to use the same tags you used on your GameSparks Virtual Goods, if they were in use.

We can create categories as shown below.

We can now create virtual items in the stores. Click on Add to create an item.

A small window will pop-up where you can supply item name and item type.

Example: Coin Pack
We will briefly cover an example of some common GameSparks functionality developers use Virtual Goods for, namely, coin-packs.

These are basically a way for the player to gain currency in order to obtain more Virtual Goods. To configure this kind of Virtual Good, set the item-type to Coins and the Currency Code should be one of the Virtual Currencies we configured in the previous topic.

Once the item-type is selected you will see a new list of parameters appear.

Purchase limit is similar to the Max Quantity parameter in GameSparks Virtual Goods, this is the maximum amount the player can own at one time.

Purchasing Virtual Goods differs from GameSparks in that you purchase AccelByte items through the Order Service API. For this topic we will deal with granting items to the player, purchases (IAPs) are covered in another topic here

To set a price for the item you can scroll to the bottom of the form to the Default Region Data section. Here is where you can set the price for your item. In this case you can do something like convert coins to gems.

Leaving the price as zero entitles the player to the item for free.

Note - Remember that you have selected the Coins item-type, then we will get real currencies in the Currency Code field.

Example: In-Game Items
We can also select In-Game items as item-type. When you choose this item-type there are different parameters than for the currency-pack example above, and an important one we should select is the Entitlement Type.

Durable entitlements are like any other Virtual Good in GameSparks. Once you have a durable item you can use it forever.

Consumable entitlements however, can only be used a certain amount of times, so selecting that option will also present you with a User Count field.

Item Bundles
Bundle is another item-type you can select when creating your item. Similar to GameSparks, this item-type can be used to group multiple items together through one entitlement.

Promo-Codes & Key-Groups
The last item-type we will cover is codes. These are used to deliver certain items based on key-groups you create for your game. These are basically the items you can gift to players if they have promo-codes. We won't cover these in this topic, but there is more information about them here.

Purchasing Virtual Goods/Items
With AccelByte, we can use In-Game Virtual Currencies or a 3rd party payment service to order Virtual Goods. Both options follow the same procedures up until the point of making an order. When the order is created successfully, the player will have to pay through payment service or pay funds from our Wallet.

In GameSparks, we can use BuyVirtualGoodsRequest to buy Virtual Goods from a 3rd party store, but we should create an order to make any 3rd party payment in AccelByte.

In-App purchases and 3rd party Virtual Goods are covered in the next topic here.

Getting Virtual Good Definitions
In comparison to GameSparks, which only allows us to get all the Virtual Goods for our player, with AccelByte, we can retrieve either a single Virtual Good, or multiple Virtual Goods.

Every Virtual Good or Item you create through the portal will have Item ID as shown below.

We can supply this Item ID in order to query the Virtual Good along with region, language.

This will enable us to extract useful information about the Virtual Good configuration on the client

AccelBytePlugin.GetItems().GetItemById("567ff00f66404a5d906d30bc7b10f45b", "IE", "en",OnGetItem);

Similarly, we can get multiple items based on specific criteria with the below function. In Item criteria, we can specify region, language, item type as shown below.

ItemCriteria itemCriteria = new ItemCriteria
{
     region = "IE", 
     language = "en",
     itemType = ItemType.INGAMEITEM
};

AccelBytePlugin.GetItems().GetItemsByCriteria(itemCriteria, OnGetItems);

Order Creation
In this example, we have created an item called ‘Sword’ which costs 100 Coins (Virtual Currency). We can see the newly created sword item in the MasterMarket store below.

First, we need to retrieve the item’s information with the help of the GetItemById() method which is discussed in the above section.

Below is an example of how to get our item’s details in Unity...

abItem = AccelBytePlugin.GetItems();

//Sword’s Item Id
string itemId = "85b1b66e9fb74880a029a37dbe661834";

string region = "IE";
string language = "en-US";

//Call to retrieve Item info
abItem.GetItemById(itemId, region, language, GetItemByIdCallback);

/// <summary>
/// Retrieved virtual item information
/// </summary>
private void GetItemByIdCallback(Result<PopulatedItemInfo> result)
{
        itemResult = result;
        Debug.Log("Populated ItemInfo" + itemResult.Value.name);
}

Now that we have the item information, we need to build and order before actually sending the order.

We can use the item information from the previous step to make an order. We need a currency code, discountedPrice, itemId, price, quantity.

int quantity = 1;

//AccelByte.Models.OrderRequest
OrderRequest orderRequest = new OrderRequest
{
       currencyCode = itemResult.Value.regionData[0].currencyCode,
       discountedPrice = itemResult.Value.regionData[0].discountedPrice * quantity,
       itemId = itemResult.Value.itemId,
       price = itemResult.Value.regionData[0].price * quantity,
       quantity = quantity
};

And now we can create an order from this order request.

abOrder.CreateOrder(orderRequest, CreateOrderCallback );

/// <summary>
/// Once item is ordered and it will return order information
/// </summary>
/// <param name="result"> Order information</param>
private void CreateOrderCallback(Result<OrderInfo> result)
{
        Result<OrderInfo> createOrderResult = null;
        createOrderResult = result;
}

You can confirm your player received the item by going to the player-manager.

For this example the user has 200 coins in the wallet before the order and does not have items.

After creating the order you can see the order details listed in the player’s account. The wallet is also updated because the Sword item cost is 100 coins.

If you want to read more about AccelByte’s internal virtual item granting process, you can find it here

Virtual Goods (3rd Party)

3rd Party Virtual Goods in GameSparks relate specifically to the ability of a player to purchase an item with a given product Id or SKU from a 3rd party store like Apple or Google Play.

The transaction is validated by the backend in order for the Virtual Good to be delivered to the player safely and without interference from the client.

For GameSparks, the flow for delivery of Virtual Goods is as follows:

  1. First we need our player to be authenticated with the platform’s client SDK, for example GooglePlay or Apple.
  2. The player will initiate a transaction with that platform using the client SDK
  3. The SDK will return some form of receipt data. This can be some form of meta-data about the transaction, it can also be a hash or a token of some kind. This is what we need to validate the transaction.
  4. The client sends this data to the appropriate GameSparks request.
  5. Under-the-hood, GameSparks will send this data to some validation API specific to the platform. It will also use some of the Integration settings set through the portal like any appIds or secrets.
  6. The platform will return validation details, along with some unique identifier for the player.
  7. Using that identifier, we can deliver the Virtual Good or Item to the player.

This is the flow we will be trying to replicate in this topic.

In some cases, these platforms use a different feature for Virtual Goods and for IAPs so keep this in mind.

Note - Anywhere we show an example of custom validation with another platform, you may need to first make sure that player has been authenticated with that platform, so check out how to do that in this topic here.

While AccelByte does have other IAP options available that overlap with GameSparks (you can check those out here), they do not have functionality specifically for IOS and Google purchases.

However, as mentioned in the section on Cloud-Code here, there is always the option to build your own backend that can integrate with their Golang or JS SDKs. This would allow you to rebuild the GameSparks receipt validation routes for Apple and Google and validate purchases that way. Another option would be to engage with AccelByte directly and have them build a custom microservice for your payment processing requirements.

Achievements

In GameSparks, Achievements are a pretty simple feature, however there are a few things we need if we want to migrate them from GameSparks to another platform.

GameSparks Achievements have the usual short-code, name and description of other features of GameSparks, but they also have the ability to deliver Virtual Goods and Virtual Currency as part of their own delivery. This is important because some of these platforms don't have this feature built in. Where possible, we will show how you can add this feature yourself with existing tools or APIs. Therefore, the next important thing these platforms need is some sort of API for Achievements, or if not, some API for creating static objects which we can add Virtual Goods and Virtual Currency to in order to create our own Achievements.

Most of these platforms do not have Leaderboard Triggers and without an API or callback from the Leaderboard, it would be quite difficult to make an automatic version of these features. However, we will mention where possible an alternative mechanism for Leaderboard Triggers that you could investigate.

AccelByte provides a few of the different features we need to migrate GameSparks Achievements however there are differences which we will point out in this topic. The main difference is in how we can deliver and trigger Achievements.

To create an Achievement through the Admin portal, you should select Achievements under the Game Management section as shown below.

You can find the Add Configuration button on the achievements page above. If we click on that option, a small window will appear to configure Achievements as shown below.

Achievements have the same Code, Name, and Description you are used to from GameSparks.

Using the Hidden option the Achievement will only appear once they get unlocked. This is a handy feature that GameSparks does not have.

We can also set two kinds of Achievements namely Incremental and Non-Incremental. Incremental, Non-incremental Achievements are set by the client and do not need a backend trigger to be delivered.

Incremental achievements are goal-based achievements and you should tie to specific Stats from the Statistics service. The state of those Stats delivers the Achievement to the player. From the AccelByte SDK you cannot reward incremental-Achievements directly, this is very important.

Incremental Achievements have to be delivered to the player through other mechanisms such as stats or other event triggers. We will therefore show a simple example for non-incremental usage below using the Unity client SDK.

Client Example
We can unlock non-incremental Achievements from the game client with a few lines of code, as below. All we need is the Achievement Code which is the “Code” param from when we set up the Achievement config earlier.

string achievementCode = "unlock-bullets-pack";
AccelBytePlugin.GetAchievement().UnlockAchievement(achievementCode, OnUnlockAchievement);

Unfortunately you can't see these Achievements from the Users section of the Admin portal so we can try to retrieve it using the API if you want to double-check.

We can retrieve any single Achievement with the following code

AccelBytePlugin.GetAchievement().GetAchievement(achievementCode, OnGetAchievement);

We will get achievement information from the backend as shown below.

We can retrieve all achievement-details that includes both Unlocked and InProgress as shown below.

AccelBytePlugin.GetAchievement().QueryUserAchievements(AchievementSortBy.LISTORDER, OnQueryAchievements);

Please note that achievements are like events, when they happen, it will trigger Rewards service to award Virtual Goods or Currency awards to the player automatically.

Leaderboard Triggers
As discussed above, one of the main differences between Accelbyte and GameSparks is how Achievements are triggered.

In GameSparks, we can trigger Achievements either through a Leaderboard or from a Cloud-Code script. In AccelByte, we have to trigger the Achievement in conjunction with the Statistics service. This is because the Leaderboard service is dependent on the Statistics service in AccelByte and you can find the Leaderboard tutorial about it here

Teams

When we talk about Teams, we must keep in mind that other platforms might call this feature something else. Teams can encompass a lot of different multiplayer features of the same name such as Clans, Guilds, Groups, Orgs, Tribes, Factions, etc., so keep this in mind. These other platforms may not call their feature Teams and that might not be what the feature is called for your game.

GameSparks Teams are one of the more complicated features of GameSparks but in essence they provide functionality to group players together into a single data structure. When driven by the SparkTeams API this feature can be used to create a variety of custom features, but out-of-the-box GameSparks provides the following functionality:

  1. Players can create or delete Teams.
  2. Players can view a list of Teams they wish to join.
  3. Players can view Teams they belong to.
  4. Players can join a Team.
  5. Players can leave a Team.
  6. At an API level there is also the ability to remove players from a Team.

These features are what we need in order to migrate Teams from GameSparks to another platform.

While not all GameSparks developers use all features of Teams, the basic needs are usually the requests above, and the same functionality through an API.

However, there are other features with GameSparks Teams that we will discuss.

Team Chat & Notifications
Team Chat is a very widely used GameSparks feature. It can be adapted for other features like lobbies or tournaments or even global announcements.

Something important to keep in mind when reviewing these other platforms is that they do not all use websockets like GameSparks does. That means that it is not as simple for that system to send notifications to other team members when something has changed, or a chat message has been sent.

We will look at how these notifications can be reproduced in these platforms where possible.

Team Chat may not exist at all as an option in these platforms, but we do cover messaging and chat in another topic here.

Team Leaderboards
In GameSparks, Team Leaderboards use the team ID instead of the player ID in order to post scores. This allows any member of the Team to post scores to a Leaderboard.

Some of these alternative platforms will not provide this feature, and without access to the underlying API controlling Teams, there is no way to reproduce this feature.

Team Data
Team data is not a feature of GameSparks, but it is a common feature for GameSparks developers to create themselves when using Teams and Cloud-Code.

Team data is created by using the team id as a field to search for Teams by in a new database collection. By referencing the team id, you can add any information you want to the Team. Common examples might be a “team-chest” where you can add all your Team’s earned currency or shared items. Extra fields for icons, descriptions or rules are also common.

Where possible we will also show how to create this feature if it is not offered by the platform out-of-the-box.

In AccelByte, the Groups service is like the Teams in GameSparks. However, the AccelByte Groups feature actually provides more functionality when compared to GameSparks.

For example, we can search for the specific group name instead of just the team ID and we can retrieve the list of groups that match with keywords supplied. We can retrieve group information for any specific player or for the player who is calling the API call. We can add and update the Group with custom data which will be discussed in the Team Data section.

With GameSparks, we can create open or private Teams. With AccelByte three are three options: open, public and private.

These options are the same for both platforms except for platforms for the public option:

There are no features like Team Leaderboards or Team Chat from GameSparks in AccelByte, however, there is a Chat feature covered in the topic here which allows you to use the Group feature in conjunction with their Lobbies service to create something like Team chat.

For comparison with GameSparks’ Teams feature, with AccelByte you can:

Note - You cannot delete a Group once it is created in AccelByte whilst you can drop a Team in GameSparks.

Group Configuration
In GameSparks, you create and configure new Teams through the portal. This is the same with AccelByte. However, there are several additional configuration steps required before we can start making a Group.

Group Roles
We need to define the various roles in a group with different permissions for each role. These roles are required in order to create a new Group. This will be explained in the following section. We can create as many roles as we want to suit our game’s needs.

From the Admin portal, you should go to the Roles section under the Group Management category as shown below. You need to click on the Add Role button on the right-hand side of the window and supply the role name when prompted by the small pop-up. We will create two roles named “Captain” and “Private”.

We will use the “Captain” role for our administrator in the Group, and everyone else in the Group can be “Private”. These are essentially members without administrator privileges.

Role Permissions
We can set permissions by clicking the view option beside the specific role we want to set, as shown below.

We can assign appropriate permissions to the roles based on the needs as shown below.

There are other ways to mix and match the privileges of these roles depending on your own game. You can check out the guide here for more information.

Group Configuration
Now that we have our roles created, we need to configure the new Group in the Admin portal.

Go to the Configurations section under the Group Management category as shown below. Click on the Initiate Configuration button. This will create a default group with a 50-member capacity.

Then, we can create a configuration with all necessary details as shown below.

There are similar fields for GameSparks features like code, name and description.

You can select the roles you created earlier using the Group Admin and Group Member fields. In our example, Captain is the Group Admin Role and Private is the Group Member Role.

Now that we have configured the group in the admin portal and it is ready to use. Players can create their own instances of this Group from the client SDK.

Creating New Groups
Using the GameSparks SDK, your players can create their own new Team with three fields, namely TeamId, TeamName, TeamType.

With Accelbyte, we can provide similar details and also include groupRegion, groupDescription and cutsomAttributes. The group-region is a string representation a C# RegionInfo name. Custom attributes will be covered in a section later in Team-Data. This is custom data you want to attribute to your Group. In the example below we added a wallet to our Group so that it can have similar attributes to the player wallet we created in previous topics.

Note - You cannot assign a group ID like team ID in GameSparks and a random one will be generated.

Your players can create a new Group using the example below.

CreateGroupRequest createGroupRequest = new CreateGroupRequest
{
            configurationCode = "group-clan",
            groupDescription = "This is My Fabulous Group",
            groupName = "OmegaSquad",
            groupRegion = "IE",
            groupType = GroupType.OPEN,
            customAttributes = new Dictionary<string, object> {
               {"Level", 0 }, {"Coins", 0 }, {"Gems", 0 }, {"XP", 0 }
            }
};

AccelBytePlugin.GetGroup().CreateGroup(createGroupRequest, result =>
{
            Debug.Log("GroupId: " + result1.Value.groupId);
            Debug.Log("GroupName: " + result1.Value.groupName);
            Debug.Log("GroupRegion: " + result1.Value.groupRegion);
});

The configurationCode field shown above is the code we gave to the Group we created through the admin portal. groupType is specified using an enumerator. Our group type is GroupType.OPEN so that anyone can join.

Using this example you will see some logs in the console once your Group is created.

You can confirm the group was created by going to the Lists option under the Group Management category of the admin portal.

Searching & Finding Groups
In contrast with GameSparks, we can search for groups by groupName and groupRegion instead of just name or type.

Note - AccelByte does not allow you to list all the groups of a certain type or config.

Below is an example of how to get a list of groups from the client SDK.

string groupName = "Omega";
string groupRegion = "IE";
int offset = 0;
int limit = 20;

AccelBytePlugin.GetGroup().SearchGroups(groupName, groupRegion, limit, offset, result =>
{
       foreach (var group in result1.Value.data)
       {
              Debug.Log("GroupId: " + group.groupId);
              Debug.Log("GroupName: " + group.groupName);
              Debug.Log("GroupRegion: " + group.groupRegion);
        }
});

You can also do partial string searches with AccelByte. For example, we searched for groups containing the word ‘Omega’ and you can see we found our “Omega Squad” group below.

Getting Member Details
There are two APIs to be aware of when it comes to getting member details. First you get the list of members and then, you generally want some more specific information about those members. We will cover that here briefly.

Group Members List
We have set up a Group with 3 members as shown below.

With GameSparks, we can retrieve Team information with ownerId, teamId and teamType. However, with AccelByte, we only need groupId.

We can retrieve our three users using the below API call.

string groupId = "60c35882c568598bf1bfe622";
abGroup.GetGroupMemberList(groupId,GroupMemberCallback);

private void GroupMemberCallback(Result<PaginatedGroupMemberList> result)
    {
        if (result.IsError)
        {
            Debug.Log("Encountered an error: " + result.Error.Message);
        }
        else
        {
            int i = 0;
            foreach(GroupMemberInformation id in result.Value.data)
            {
                Debug.Log("Group Member" + i++);
                Debug.Log("UserId" + id.userId);
            }
        }
    } 

And we can see our member IDs logged in the console.

BulkUsersPresence
This is similar to the above API call, but this is mainly helpful to retrieve information about the status and presence of a user.

userIds[0] = "b18989ecb007482eb9b138exxxxxxx";
userIds[1] = "004ffcdf69ad47a096a1e2e6xxxxxxx";

AccelBytePlugin.GetLobby().BulkGetUserPresence(userIds, BulkUserPresenceCallback);

private void BulkUserPresenceCallback(Result<BulkUserStatusNotif> result)
{
    if (result.IsError)
    {
        Debug.Log("Encountered an error: " + result.Error.Message);
     }
     else
     {
         foreach (UserStatusNotif BUStatus in result.Value.data)
         {
                Debug.Log("UserId" + BUStatus.userID);
                Debug.Log("namespace" + BUStatus.namespace_);
                Debug.Log("lastseenAt" + BUStatus.lastSeenAt);
                Debug.Log("availability" + BUStatus.availability);
                Debug.Log("activity" + BUStatus.activity);
                Debug.Log("online" + result.Value.online);
                Debug.Log("offline" + result.Value.offline);
                Debug.Log("invisible" + result.Value.invisible);
                Debug.Log("busy" + result.Value.busy);
          }
      }
}

Here you can see the values for these different parameters.

The difference between GetGroupMember() and GetBulkUserPresence() is the type of response you get. If you want to retrieve information regarding Group Member information related to User then you can use GetGroupMember() call. If you want to know the status of the player in the group, you would use GetBulkUserPresence().

Group Interaction
There are several different APIs used in the regular flow of joining, leaving, and managing Groups. These are like GameSparks so we will only briefly cover them.

Join Group In GameSparks, we have the JoinTeamRequest to send a request to join a team. Similarly, we have the JoinGroup API call for AccelByte.

We will simply supply groupId but this is not the case in the GameSparks version where we should supply ownerId, teamId and teamType.

When we call this method from the client, depending on the type of the group (open, public or private), a JoinRequest will be sent out to the Admin.

If the team type is open, as in our example, then players will join instantly. otherwise, the administrator has to approve the request.

Below is an example of the API call for JoinGroup.

string groupId = "60c35882c568598bf1bfe622";
abGroup.JoinGroup(groupId, JoinGroupCallback);

private void JoinGroupCallback(Result<JoinGroupResponse> result)
{
        if (result.IsError)
        {
            Debug.Log("Encountered and error: " + result.Error.Message);
        }
        else
        {
            Debug.Log("Joined Group");
            Debug.Log(result.Value.status);
        }
 }

The response is shown below.

Leave Group
We can leave the team in GameSparks by using LeaveTeamRequest. In AccelByte, we call LeaveGroup to achieve the same functionality. We need three parameters, ownerId, teamId and teamType in GameSparks; while we do not need any parameters to call the LeaveGroup call in AccelByte.

Below is an example of the API call for LeaveGroup.

abGroup.LeaveGroup(LeaveGroupCallback);
private void LeaveGroupCallback(Result<GroupGeneralResponse> result)
    {
        if (result.IsError)
        {
            Debug.Log("Encountered and error: " + result.Error.Message);
        }
        else
        {
            Debug.Log("Left the Group");
        }
    }

We get a response as shown below.

Group Management
There are several API calls available for group management which are listed below. We will not cover these in detail here as they are not available in GameSparks, however these are great features to use in addition to your current GameSparks implementation and you can find more information about them here.

Admin Calls
Some API calls are only usable by administrators of the Group. As discussed earlier these are defined using the roles set on the group-config when it was created.

You can find more information about these administrator calls here.

Example: Team Data / Group Attributes
Although GameSparks does not have Team Data as a feature out-of-the-box, it is a very common feature that is custom-built by GameSparks developers on top of the existing Teams API.

This is usually a “TeamData” collection that uses the team ID as the query field. Using Runtime-collections or GDS you can append any data to a Team that you like.

Since this is a very common use case in GameSparks, we will cover how to reproduce this here.

In AccelByte, we usually refer to Custom Attributes when we speak about Team Data. We can easily add Custom Attributes to the Group through API calls or when the Group was created by the player as we have shown before.

We have created a group called “Omega Squad“ in one of the earlier sections. We will show the code below to create a group with custom attributes for reference.

We will add these attributes below.

We can update the custom data listed above with the following API call.

string groupId = "60c35882c568598bfxxxxxx";

var emblem = new List<int>(){ 15, 10, 0 };
customAttributes = new Dictionary<string, object> {
       {"Emblem", emblem },{"Level", 1 },{"XP", 1200}, {"Coins", 123 }, {"Gems", 15 } }


AccelBytePlugin.GetGroup().UpdateGroupCustomAttributes(groupId, customAttributes, result =>
{
      Debug.Log("Update Success!");
});

And you can view those changes in the Groups section of the admin portal.

After the update, the Omega Squad group would appear as shown below in the admin portal.

Group Notifications
With AccelByte there is a feature called Group Notifications which is similar to Team Notifications in GameSparks. However, as the Group Service is not using a socket for communication, you cannot get these notifications asynchronously. Instead, the notifications system for Groups is tied into the Lobby Service.

We will not cover that setup in this topic, instead you can find more information on Lobbies here and additional details on the notifications API at the end of the section on Groups here.

Player Manager

In this topic we are going to cover how player management works with these different platforms. What we mean by player management is any kind of form or tool which allows you to search for a player by user-name, email or id and then view or change some attributes from the back-office.

These tools can be quite complex, especially as GameSparks allows developers to create custom manage-screens where they can add any functionality they want. However, in alternative platforms we don't always have this flexibility, so we have to go with what they offer us. There is an exception to this where we could create our own manage-screen outside of the platform using custom-build tools. There is a topic here which covers that.

There technically isn't a GameSparks-official player manager anymore, but in some old versions of the portal you would get the default “Player” manage-screen where you could search for player and update certain out-of-the-box features so this is what we are going to be looking for.

From the Admin portal, we should go to the Users category under Users Management which will bring us a new page to list players based on three filters as shown below. We can get any player based on their Email, UserId, Creation Date etc.

We will see a list of players based on preferences in the earlier step as shown below.

We can see the player dashboard by clicking on the “view” option just right of each player.

This will bring up your chosen player’s dashboard as shown below. Player account details like orders, wallet, entitlements, linked accounts, sessions etc will be displayed here.

Player Wallet
There is a separate section for wallets specific to the player where you can credit or debit any desired amount with virtual currency as shown below. You can find the dedicated wallet management section named “Wallets” under the E-Commerce section in the Admin portal.

You need to have some Virtual Currency credited to the player before your player’s wallet will appear. The wallet needs to be initialized first. Check out the topic on Virtual Currency here for more details on testing out the player wallet feature.

Entitlements/Items
In the screenshot below you can see our user has two items. These are listed in the “Entitlements” tab, which is another word for Items or Virtual Goods used by AccelByte.

Orders
Orders are something you will not find in GameSparks but they are discussed in our topic on Virtual Goods here.

You can see the various orders made by your users over time as shown below.

Stats
In order to see player stats, we need to go to a dedicated statistics window in the Admin portal.

Under Game Management, you can find the Statistics section. If you click on the Statistics Value panel, you can see Player and Global options below and these are discussed in our topic on Leaderboards here

We need to enter a player userId,or Email, or Username and this will open the player stats as shown below.

Chat Messaging

In this topic we are going to cover how to migrate Chat and Messaging from GameSparks to these destination platforms. In GameSparks these two features are often the same thing and are commonly grouped together into GameSparks’ Team Chat feature. However, this feature won't exist on destination platforms and may instead be grouped into another feature or separate features (Teams and Chat).

For migration of general Chat functionality we need the following features to be available on the destination platform:

Chat
This can be any kind of asynchronous messaging between players connected to a common group. This group could be a team as is the case in GameSparks Team Chat, but it could also be a lobby or chat group unrelated to a team. There might also be global chat features.

Chat is not always handled like it is in GameSparks. In GameSparks Chat and Chat Messages are the same. You can get a list of all your Chat Messages at any point in time and see your Chat history. In some alternative platforms Chat is ephemeral, so you might be able to see your history for the session or lobby you are in at that moment in time. After that session is over you will not be able to see that chat history again.

Therefore we need to investigate if there is an alternative to this feature available for the destination platform too.

Messaging & Inbox
This is different from Chat as it involves how to get a list of messages for the player. Like Chat, this feature is often used to send messages between players, but also for the game-team to send messages to players. This feature is often called the player inbox because of this. It is another way to communicate with the player where they can also see a list of past messages.

The main difference with this feature and Chat is it is not designed to get messages in real-time. This feature would use a call to get all the player’s messages and then the frontend would need to sort those messages in the UI to show which are read and unread.

Messaging API
Along with Team Chat, GameSparks exposed a lot of the underlying Chat APIs in Cloud-Code using the SparkMessage API. This allows GameSparks developers to create their own custom Chat and inbox features to fit their requirements.

Examples of this might include profanity filtering, automatic banning from chat groups, inbox systems for player-to-player mail, news and announcements.

This feature may not be available with all alternative platforms, but we will cover any workarounds where possible.

Note - We will not be covering Push Notifications in this topic, though we will point out where that feature is available on the destination platform and provide links to documentation if available.

Although AccelByte does have a chat feature, it does differ from GameSparks. The biggest difference is that chat is separate from the general “messaging” API and are not coupled together like GameSparks’s Team-Chat and SparkMessage API.

The Party-Chat feature works the same as GameSparks’ Team-Chat. We have already covered parties in some detail in the Matchmaking topic here but we will go through this again briefly with some examples. Personal Chat is pretty much the same, but is used for player-to-player messaging.

All of these features work through the Lobby Service which we have covered in the Matchmaking service and which allows players to get notifications through a websocket.

It is important to note that this Party-Chat and Global-Cat is ephemeral, so you cannot get a list of messages or chat history like you can using GameSparks’ requests or SparkMessage API. This is specifically for chat-groups or chat-lobbies. However, Personal-Chat does allow you to get chat history so it could be used to replace GameSparks messaging APIs if you have something like a player-inbox feature custom-built.

Party Chat
For Party-Chat, players can send messages to other players in a party which is similar to Team-Chat or Chat messages in GameSparks.

Players should already be grouped into a party to enable this functionality. We have already covered the tutorial on how to set up parties in the MatchMaking topic linked above. Players in a party need to send party invitations and players can join the room. There are other features available in Party service and if you want to read more about it, you can refer here.

Note - Players need to be connected to the Lobby service to send and receive invitations.

Just as a recap of the other topics where we dealt with parties, players can send an invite to a specific user with the below API call.

abLobby = AccelBytePlugin.GetLobby();
abLobby.Connect();

//Sending an invite
abLobby.InviteToParty(inviteeUserId, InviteToPartyCallback);

/// <summary>
/// Invitation callback after sending out an invite
/// </summary>
private void InviteToPartyCallback(Result result)
{

       if (result.IsError)
       {
          Debug.Log($"Error. Code: {result.Error.Code} Reason: {result.Error.Message}”);
       }
       else
       {
           Debug.Log("Successfully invite an invitee");
       }        
}

Next, we need some way to notify players that they have been invited to the party. This can be done using the InvitedToParty delegate below.

// Method subscription to notification event
abLobby.InvitedToParty += InvitedToPartyCallback;

//Notification information regarding party invitation
private void InvitedToPartyCallback(Result<PartyInvitation> result)
{
    partyInvitation = result.Value;
    if (!result.IsError)
    {
            Debug.Log($"[Notification] Invited by:{result.Value.from}");
    }
}

Below is the response we received when we sent an invitation to another player.

A player with an invitation can accept the invitation and join the group with the below API call.

abLobby.JoinParty(partyInvitation.partyID, partyInvitation.invitationToken, JoinPartyCallback);

/// <summary>
/// Party Information from join party callback
/// </summary>
/// <param name="result">PartyInfo like party leader, party members, invitees etc.</param>
private void JoinPartyCallback(Result<PartyInfo> result)
{
      if (result.IsError)
      {
           Debug.Log($"Error. Code: {result.Error.Code} , Reason: {result.Error.Message}");
      }
      else
      {
           Debug.Log("Successfully join a party");
            int i = 0;
           foreach(string partyMember in result.Value.members)
            {
                i++;
                Debug.Log($"Party Member{i}: {partyMember}");
            }
      }
}

If the request was a success, you will see the two members printed in the console.

Now that we have created a party, those party members can chat with each other.

As you might expect, we can do two things in Party-Chat,

There is an example of these API calls below...

//Sending a party message
abLobby.SendPartyChat("Hello, party people!!", SendPartyChatCallback);

/// <summary>
/// Callback after sending out a message to other party members
/// </summary>
private void SendPartyChatCallback(Result result)
{
     if (result.IsError)
     {
         Debug.Log($"Failed to send a private message! Code: {result.Error.Code}");

     }
     else
     {
         Debug.Log("Successfully send a party message!");
     }
}

Other members who are in the party will receive the message. Party members need some way to notify themselves when the message arrives. There is an example below of how to subscribe to the party to get notifications.

//Method subscribing to message received notification event
abLobby.PartyChatReceived += OnPartyChatReceived;

/// <summary>
/// Method will trigger when the player receives a party message
/// </summary>
/// <param name="result">Sender, receiver and actual message</param>
private void OnPartyChatReceived(Result<ChatMessage> result)
{
     Debug.Log($"{result.Value.from} sent a private message to you {result.Value.to}");
     Debug.Log($"Message : {result.Value.payload}");
}

In our example you will remember that we only have two players so below is the message received by a second party member.

Note - Your game namespace must have chat enabled in Lobby configuration under the “Lobby and Matchmaking” section in the Admin portal to send messages.

Personal Chat
Although Party-Chat can send messages to party members, players cannot keep track of chat history. This is by design, as chat is meant to be real-time and ephemeral as we already mentioned. In other-words the player must be connected to the Lobby Service in order to get messages.

With the Personal-Chat feature, we also get real-time notifications, but we can also request chat history as messages are stored after your player disconnects from the Lobby Service.

However, there is no client API for getting chat-history from the client, so in order to do this we need to request chat history using a HTTP.

As with Party-Chat, there are two requests we can make from the client for Personal-Chat.

The player can send a personal chat message to any desired player with their userid as shown in the below API call.

string userId = "d380d40a08d1481ba06b1dcdceXXXXX";
string chatMessage = "Hi! How are you?";

// Sending a personal chat message to the player
abLobby.SendPersonalChat(userId, chatMessage, OnSendPersonalChatToPlayer);

/// <summary>
/// Callback after sending a personal chat message to any player
/// </summary>
private void OnSendPersonalChatToPlayer(Result result)
{
     if (!result.IsError)
     {
         Debug.Log("Message sent successfully.");
     }
     else
     {
            Debug.Log($"Failed to send a private message! Code: {result.Error.Code}");
     }
}

The player who is entitled to receive the message needs to subscribe to receive notifications by assigning a delegate to the PersonalChatReceived callback.

We can easily subscribe to the notification event as shown below.

//Method subscription to the nofication
abLobby.PersonalChatReceived += OnPersonalChatReceived;

/// <summary>
/// Method will trigger when the player receives any personal chat message
/// </summary>
/// <param name="result">Information regarding sender, receiver, actual message..etc</param>
private void OnPersonalChatReceived(Result<ChatMessage> result)
{
       Debug.Log($"{result.Value.from} send a private message to you                    {result.Value.to}");
       Debug.Log("Message: " + result.Value.payload + "\n");
}

Using this example we can see the message in the Unity console.

As mentioned already, accessing is not done using the client SDK.

There is another flow to achieve this functionality as shown in the diagram below.

Note - We have used HTTP web-requests from system.net class and NewtonSoft JSON for deserialization. We will not go into details about this process but you can see more information about web requests here.

Getting The Access Token
We need to call the POST method to send data to retrieve an access token of the user. There are two important parameters highlighted in the code-example below@

/// <summary>
/// Retrieving an accesstoken for the user
/// </summary>
public void TokenGrant()
{
        WebRequest request = WebRequest.Create("https://demo.accelbyte.io/iam/oauth/token");
        request.Method = "POST";
        request.Headers.Add("Authorization", "Basic" +  MWU2xxxxxxxxxx);
        request.ContentType = "application/x-www-form-urlencoded";

        string postData = "grant_type=password&username=xxxxxxxx&password=xxxxxxxxxx&namespace=xxxxxxxx";

        byte[] byteArray = Encoding.UTF8.GetBytes(postData);
        request.ContentLength = byteArray.Length;
        Stream dataStream = request.GetRequestStream();
        dataStream.Write(byteArray, 0, byteArray.Length);
        dataStream.Close();
        WebResponse response = request.GetResponse();
        Debug.Log(((HttpWebResponse)response).StatusDescription);

        using (dataStream = response.GetResponseStream())
        {
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();
            Debug.Log(responseFromServer);
            var parsedObject = JObject.Parse(responseFromServer);
            var accesstoken = parsedObject["access_token"].ToString();
            Debug.Log(accesstoken);
            LoadPersonalChatHistory(accesstoken);
        }
        response.Close();
}

Once we have received the access token, we need to get the chat history. We have an example of this using a custom method called LoadPersonalChatHistory() which we will cover below.

As with the previous HTTP call there are two parameters highlighted in this request:

/// <summary>
/// Model for chat history JSON response
/// </summary>
class ResponseDataAB
{
        public int id { get; set; }
        public string from { get; set; }
        public string to { get; set; }
        public string payload { get; set; }
        public int receivedAt { get; set; }
}

/// <summary>
/// Loading the chat history
/// </summary>
/// <param name="accesstoken"> Access token from TokenGrant function</param>
public void LoadPersonalChatHistory(string accesstoken)
{
        Debug.Log("Loading personal chat history");
        WebRequest request = WebRequest.Create("https://demo.accelbyte.io/chat/namespaces/{your-namespace}/users/{CallerUserId}/friend/{ReceiverUserId}");

        request.Headers.Add("Authorization", "Bearer " + accesstoken);
        request.ContentType = "application/json";
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        Stream dataStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(dataStream);
        string responseFromServer = reader.ReadToEnd();
        var responseData =    JsonConvert.DeserializeObject<List<ResponseDataAB>>(responseFromServer);
        responseData.ForEach(res => Debug.Log(res.payload));

        reader.Close();
        dataStream.Close();
        response.Close();
}

Note - The above HTTP request can only work if the user has the permission NAMESPACE:{namespace}:USER:{userId}:CHAT [READ].

You can also see in the example above that we have created a new class called “ResponseDataAB”. This is so that we can parse the JSON string response to an object we can work with in AB. You can create your own custom object of course, but, if you have gone through all the steps mentioned above, you will see your player’s chat-history in the Unity console.

Global Chat
As a minor note which we won't cover in this topic, AccelByte provides another feature of their Chat service called “Global Chat”. This feature allows all players in the game to communicate.

Downloadables

Downloadables are a pretty straight-forward feature of GameSparks. It allows developers to host static files through the platform which can be downloaded with out-of-the box requests or APIs from the client. Downloadables in GameSparks also come with a few limitations such as the size of the files which can be hosted, other platforms might have similar limitations.

In essence, Downloadables are a CDN (Content Distribution Network) which is served using AWS S3. You upload files through the portal and give them a unique code. When a player requests the file, GameSparks will request a short-lived URL where the player can download the file and return that to the player. It is then up to the client to download that file.

In this topic we will discuss how other platforms deal with their CDNs and hosting static files. Because of the simplicity of this system, it is not necessary that these alternative platforms provide a CDN or alternative feature for Downloadables. There are other options available and creating your own system is not difficult and does not require much maintenance. You can see one approach here using AWS S3 in a similar way to GameSparks.

AccelByte does not currently offer any CDN alternative to GameSparks’ downloadables, however it does have an alternative to GameSparks’ Uploadables feature where players can upload their own content.

We won't cover uploadables in too much detail here but you can see the APIs for AccelByte’s Slots feature here.

The process is similar to GameSparks:

  1. [Post] - Create a new slot. You can now get the URL and upload the binary into that slot
  2. [Get] Get the URL
  3. [Update] Allows you to upload/overwrite the data
  4. [Delete] Removes the data from the slot

While AccelByte does not currently have an alternative to GameSparks Downloadables it is possible to create your own CDN using AWS services. We have a guide on this here. This would require either a custom microservice or your own backend with the AccelByte Goland or JS SDK integrated. There are more details on this process in our topic on Cloud-Code here.

Emails

In this topic we are going to cover some of the options these alternative platforms offer related to email features.

GameSparks does not have its own email service but instead integrates the SendGrid API through the Spark.sendGrid() API. SendGrid is a very lightweight service to integrate, so in most cases it can be integrated via C#, JS, etc. You can also integrate it in the client if there is no alternative option for these alternative platforms. However, that would be very insecure as it would mean exposing your API credentials after which anyone could use them.

If the alternative platform does not have a Cloud-Code feature you could protect your credentials by sending the request to an AWS Lambda function first, and then to the SendGrid API.

In some cases, these alternative platforms also have their own email features which can be used for registration emails and password reset, so we will mention those here too.

AccelByte does not have an integration with SendGrid directly, however, it does come with some of the common functionality GameSparks developers use SendGrid for such as registration emails and password reset, so we will cover those features here.

New players on AccelByte are always sent a welcome email if they are registered through the normal username & password registration flow.

From there, if your player wishes to reset their password there are two APIs they need to call in order:

  1. Request Reset Password Code
  2. Use code, along with the new password to reset the user’s password.

Through the Unity SDK, this is very simple.

//Request reset password code
abUser.SendResetPasswordCode("username", OnResetCode);

public void OnResetCode(Result result)
{
  // password reset code returned //
}
//Reset Password
abUser.ResetPassword("code", "username", newPassword, OnResetPassword);

public void OnResetPassword(Result result)
{
  // password reset //
}

You can also achieve this through the REST API if you need to create your own flow or host your own reset web-pages or forms.

  1. Request Code
  2. Reset Password

SendGrid API
Because AccelByte does not have any direct integration with SendGrid like GameSparks does, in order to migrate your current SendGrid functionality you will need to integrate their API into another application. SendGrid do have a C# SDK which you can see an example of in the Beamable section of the Emails topic, however, this is not advised as it would expose your apiKey. If the client was hacked, anyone could use your apiKey for their own applications.

There are a number of alternatives to sticking the SDK in the client.

Custom Microservice
AccelByte provides services for building custom microservices to meet your requirements. There is more about this in the Cloud-Code topic here. This microservice would be deployed alongside your environment so you have access through the SDK.

Custom Game-Server
In a case where you need more flexibility but do not want a custom microservice, you can create your own game-server and implement the SendGrid SDK into that.

Lambda & API Gateway
AWS Lambda and API Gateway are AWS serverless services. Lambdas allow you to run your scripts in the cloud which would be easily achieved with SendGrid’s JS SDK. API Gateway allows you to connect the execution of this script to a REST API, which would allow you to send messages from the client if needed, but your apiKey would not be exposed to the client.

Matchmaking

GameSparks MatchMaking is a very flexible and powerful feature. It incorporates a number of complex features like Real-Time servers and Matchmaking Scripts. These features may not be available for alternative platforms, so in this topic we are going to deal with two fundamental components needed to migrate your existing Matchmaking feature.

Thresholds
The basic Matchmaking config in GameSparks consists of a min and max set of players you would like to match with and a set of thresholds.

These thresholds are used to create conditions upon which matchmaking decisions can be made.

In GameSparks, these thresholds are pretty simple and are controlled by a single parameter called “skill” which is passed in through the MatchMakingRequest. Alternative platforms have similar functionality for their Matchmaking, though with different setup and API calls to enter a player into Matchmaking.

Matchmaking API
Oftentimes Matchmaking needs additional functionality in order for the feature to comply with the designers needs. There are a number of ways to do this discussed in the following section, but one way is with the SparkMatch API.

SparkMatch allows developers to control an existing instance of a match, add or remove players or edit the match-data payload. This is an extremely powerful tool to create custom match features however, it is not present for many alternative platforms. Where possible we will demonstrate workarounds for this.

Other Features
As mentioned above, the SparkMatch API can be used to control and extend the matchmaking feature, but it is also possible to add custom context to the match outside of the “skill” value passed into the MatchMakingRequest. You can do this with the “participantData” field, for example, matching only players from a specific region.

{
  "@class": ".MatchmakingRequest",
  "participantData": {
      "region" : "eu"
  },
  "skill": 0
}

This may not be possible in all alternative platforms but we will cover it where possible.


Where an even more complex set of Matchmaking rules is required you might be using matchmaking scripts to manipulate player and match data during matchmaking. This is a very complex feature that is not common on other platforms. Where there is some overlap between this feature and the destination platforms matchmaking offering we will show examples, however, something to keep in mind is that where these platforms offer a “cancel matchmaking” API, you can do a lot of this work client-side by setting up custom timers that will cancel a matchmaking request after a given period of time and then issue another matchmaking request with different params

GameLift FlexMatch
If these alternative platforms do not offer a solution to your existing Matchmaking configuration it is worth checking out AWS FlexMatch. FlexMatch offers a matchmaking service with a high-degree of configurability. In comparison with GameSparks, matchmaking criteria are designed through a script which allows you to add custom data to the match-instance and the player’s individual matchmaking ticket. You can also change criteria over time as with GameSparks thresholds and matchmaking scripts.

You can get more information on FlexMatch here and you can see an example of how to create a serverless implementation of the service here.

AccelByte has a pretty robust matchmaking feature which allows you to create something like GameSparks’ thresholds along with more complex matchmaking rules.

Something that is important to explain about AccelByte’s matchmaking feature is that it is designed around their Lobby and Party feature. This can make it somewhat confusing when trying to understand which feature comes first in your implementation. We will cover these features in this topic with a simple player-to-player matchmaking example.

Something else to note is that AccelByte’s matchmaking feature uses party-to-party matching. In contrast to GameSparks, this means that you match groups of players to other groups of players instead of players to player.

This is a great feature and opens up matchmaking to a lot more possibilities, however, it can come across as though you aren't able to perform simple player-to-player matchmaking. This is not the case, you can get around this by creating a party with a single player and starting the matchmaking process that way. We will show an example of this process in this topic.

Important - Before we continue it is important to note that while AccelByte has a very good matchmaking feature with a number of similarities to GameSparks, it is designed to be incorporated with their multiplayer service. It is not necessary to do this as you will see below, but it does mean that you cannot get a list of players in your match without some external tracking. This may rule AccelByte matchmaking out as a possible migration solution.

The Lobby Service
In Accelbyte, the Lobby Service is a set of APIs that integrates with a number of other social features. The name of this feature can be somewhat misleading as it assumes that a player can set up and join different lobbies where they can wait until a game-session starts and possibly also chat with other players while they wait.

The Lobby Service is actually global. When you connect to the Lobby Service it allows you to connect to or create parties and you can start Matchmaking from there.

The Lobby Service is also used as part of the Groups Service to get updates about a Group like when a player has joined or sent an invite. It is basically a way to subscribe to different components that need asynchronous updates or messages similar to GameSparks web-sockets. The Lobby Service itself is actually using web-sockets.

This is important because conceptually, what you would consider to be a “Lobby” is more closely comparable to AccelByte’s party feature. The Lobby Service is the service which facilitates all this asynchronous messages and updates like parties, group-notifications and matchmaking notifications, etc.

We therefore start with the Lobby Service before we can set anything else up.

Setup Lobby Config
We need to configure the Lobby Service before connecting to it. We can configure it from the Admin Portal.

In Admin Portal, we should go to Lobby Configuration under the Lobby and Matchmaking category as shown below.

As you can see, we are able to choose to auto-kick players when they are disconnected and specify the number of players that should be in a party.

And that’s it! We are ready to start using the Lobby Service.

Joining The Lobby Service
We can connect to AccelByte’s Lobby Service with just a simple API call as shown below.

private Lobby abLobby;

abLobby.Connect();

We have events to include some functionality when Something happens in AccelByte. For example, once we are able to connect to the Lobby service then create a party. We will see a sample example to demonstrate events.

abLobby.Connected += OnConnected;

private void OnConnected()
{
        Debug.Log("Connected to the AccelByte’s Lobby service");
}

Parties
As mentioned before, with AccelByte, we need to create parties in order to make use of the Matchmaking Service. We can create parties with the help of Lobby Service because we have already mentioned Lobby Service includes social features.

Creating A Party
Below is an example of how to create a party from the SDK.

private Lobby abLobby;
abLobby.Connect();

abLobby.CreateParty(CreatePartyCallback);

private void CreatePartyCallback(Result<PartyInfo> result)
{
        if (result.IsError)
        {
            Debug.Log($"Error. Code: {result.Error.Code}, Reason: {result.Error.Message});
        }
        else
        {
            Debug.Log("Successfully created a party");
        }
}

Therefore, if you wish to replicate something like a GameSparks MatchmakingRequest you can always create a single-user party immediately after connecting to the Lobby Service as shown below.

private Lobby abLobby;
abLobby.Connect();
abLobby.Connected += OnConnected;

private void OnConnected()
{
        Debug.Log("Connected to the AccelByte’s Lobby service");
        abLobby.CreateParty(CreatePartyCallback);
}

private void CreatePartyCallback(Result<PartyInfo> result)
{
        if (result.IsError)
        {
            Debug.Log($"Error. Code: {result.Error.Code}, Reason: {result.Error.Message});
        }
        else
        {
            Debug.Log("Successfully created a party");
        }
}

Additional Party Functionality
The AccelByte Party feature has more functionality than we will not cover in this topic. You can read about those here.

Below are the other API calls available for Party service in AccelByte.

These are similar to the functionality available from the Groups/Teams feature which we covered in a topic here, however, they are different and parties should be treated more like a lobby where players can be grouped together before starting a game-session.

Matchmaking
The next step in order to get these parties into matches is to configure the Matchmaking Service in the Admin portal or through REST calls. We are not going to cover REST calls in this section as it is easier to demonstrate this setup through the portal.

Before we do that, we need to have at least one Stat configured as a prerequisite to the Matchmaking service.

Matchmaking Statistics
The stat used by the Matchmaking Service is essentially like the “Skill” field that GameSparks uses for its matchmaking. The difference here is that the stat is not passed into the request, but is instead handled server-side as it exists on the player’s account.

We have already covered how to create a stat with Statistic service in the Leaderboard topic available here

We will use LEVEL as the stat for this example.

Matchmaking Configuration
From the Admin portal, navigate to the Matchmaking Ruleset section under the Lobby and Matchmaking category.

In the Matchmaking window, we need to click on the Add Configuration button on the right hand side of the window.

This will bring up a small popup window where we need to supply valid information regarding matchmaking configuration.

The form above is an example of how to configure this match for 1v1 matches, but you can have any different game modes you want.

Rulesets
The next step is to set up some Rules Sets.

Rule sets are similar to GameSparks thresholds as they define a set of rules which control what values of the stat are acceptable when forming the match.

Similar to thresholds, you can set multiple rules which can be configured to change over time. “Distance” defines the relative value of the stat, similar to GameSparks, however, that is the only option you have, there is no percentage option.

With AccelByte there are two rules-sets you can choose from. The Flexing Rules option at the bottom is configured the same as the Matchmaking Rules above it, however, the flexing rules can be configured to kick-in when the service is having trouble finding a match for the player.

Something important to note is that you need to include the StatCode that we have created earlier.

Starting The Matchmaking Process
Only a party leader can start the Matchmaking process. This is usually done after players join the party according to the game-mode configuration.

Or you can simply start the process with the help of the below API call.

abLobby.Connect();
var channelName = "1vs1";
abLobby.StartMatchmaking(channelName, StartMatchMakingCallback);

/// <summary>
/// Callback to know the status of the matchmaking process
/// </summary>
private void StartMatchMakingCallback(Result<MatchmakingCode> result)
{
        if(result.IsError)
        {
             Debug.Log($"Error. Code: {result.Error.Code}, Reason: {result.Error.Message});
        }
        else
        {
            Debug.Log($"MatchMakingCode : {result.Value.code}");
        }
}

Here, the channelName field is the name of the Ruleset we just created.

If we received the code “0” in the response enum it means our request is successfully received and passed to the matchmaking queue.

Below is an example of our 1v1 match config, along with the previous example of how to create a 1-player party so that we can immediately kick off matchmaking for a 1v1 game.

private Lobby abLobby;
abLobby.Connect();
abLobby.Connected += OnConnected;

/// <summary>
/// Callback when we have successfully connected to the Lobby Service
/// </summary>
private void OnConnected()
{
        Debug.Log("Connected to the AccelByte’s Lobby service");
        abLobby.CreateParty(CreatePartyCallback);
}

/// <summary>
/// Callback when we have successfully created a party
/// </summary>
 /// <param name="result">Party information like party leader, members. etc<param>
private void CreatePartyCallback(Result<PartyInfo> result)
{
      if (result.IsError)
      {
          Debug.Log($"Error. Code: {result.Error.Code}, Reason: {result.Error.Message});
      }
      else
      {
         var channelName = "1vs1";
         Debug.Log("Successfully created a party");
         abLobby.StartMatchmaking(channelName, StartMatchMakingCallback);
      }
}

/// <summary>
/// Callback to know the status of the matchmaking process
/// </summary>
private void StartMatchMakingCallback(Result<MatchmakingCode> result)
{
        if(result1.IsError)
        {
             Debug.Log($"Error. Code: {result.Error.Code}, Reason: {result.Error.Message});
        }
        else
        {
            Debug.Log($"MatchMakingCode : {result.Value.code} ");
        }
}

MatchFound Callback
We need some way of getting updates about the progress of the match. There is an optional callback we can configure for getting matchmaking results when the match has been found.

abLobby.Connect();
abLobby.MatchmakingCompleted += OnMatchMakingCompleted;

/// <summary>
/// Notification to inform matchmaking process is completed
/// </summary>
/// <param name="result"> matchmaking process status and matchId</param>
private void OnMatchMakingCompleted(Result<MatchmakingNotif> result)
{
      if (result.Value.status == "done")
      {
            Debug.Log("Match found");
      }
}

Using Matchmaking Callbacks
Ideally you can use these callbacks to perform some custom logic when a match has been found. In GameSparks this callback would contain the matchId along with a list of players or playerIds you can group together. In AccelByte you will get a matchId but you cannot get the list of players.

You will therefore need to create your own solution for getting these players and their Ids. This would need to be a custom solution so check out the section on Cloud-Code here for more information.

Cancel Match
Finally we will show how a player can cancel the Matchmaking process once it has been started. We can simply call the below API call to do the same.

 var channelName = "1vs1";

 abLobby.CancelMatchmaking(channelName, result =>
{
     Debug.Log(string.Format("Cancel matchmaking response {0}",result.Value.code));
});

We have started the matchmaking process and immediately cancelled for demonstration purposes with the above call and below is the response received. When we receive a code ‘0’, generally, it means that the process is terminated.

Cloud Code

GameSparks Cloud-Code is actually a very broad topic to discuss from the point of view of migration. Alternative platforms to GameSparks have not been built with portability in mind, they focus on their own interpretation of Cloud-Code, so migrating Cloud-Code will not be a case of copy-pasting your existing from one platform to another. In some cases these platforms are also even using a different programming language to GameSparks.

In this topic we will look at what the differences between GameSparks Cloud-Code and what these alternative platforms have to offer, and how to adapt your existing code to fit how these platforms approach Cloud-Code.

In order to assess the complexity of migrating your own Cloud-Code we have split this topic into a list of the required features needed to replicate most GameSparks Cloud-Code use cases:

  1. Create custom scripts This means the platform allows developers to create totally new APIs for their game and not just use the out-of-the-box or modify existing APIs. You will need to create something totally from scratch.
  2. Send and receive custom data to and from these scripts GameSparks developers do use Cloud-Code to get specific player information or static data from MetaCollections, but more often they need the server to perform some actions or validations before returning a result. We therefore need to send and receive custom data and not just execute some code on the server.
  3. Get/Set Player Data Script APIs should be able to load currently stored player data for validation, update this data and save it back to the player. Ideally, we should be able to do this for any playerId through the Script API, not just the player that called the script. We need this for things likeTeams or Friends features.
  4. Get Static Game Data - MetaCollections We need a replacement for GameSparks MetaCollections as these are widely used by developers to load static game data. This could include item tables or game-configuration data like live-ops events, localization, etc. We also want these alternative data stores to be cached so they are optimized for performance.
  5. Send HTTP Requests A very common use-case for GameSparks Cloud-Code is to connect to another service like a 3rd party payment, auth or other game service. The ability to send HTTP requests is very important for extending the current API offering of the platform.
  6. Receive HTTP Requests Similar to sending requests, if we can receive data from 3rd party services we can extend the platform ourselves. This is also very common for payment validation, advertisement campaigns and email validation.

Key Differences
As already mentioned, none of these alternative platforms have a way to directly port GameSparks code into their Cloud-Code system, so we have to work with what they offer. This section covers the main differences you need to be aware of for these platforms. We go into more details on the specific differences in each platform's own section.

Player Data
With GameSparks there are several methods for storing player data we need to consider. Most developers will also use a mix between these two methods which might not be adaptable to the new platform so this is something to consider.

Player ScriptData
This would be where you are storing your player data on the SparkPlayer object in either scriptData or privateData. This uses the player system-collection and is also cached for better performance. If you are storing your current data using the SparkPlayer API you will need something that can store custom JSON. Remember that you can also store this as a JSON string if the platform doesn't allow for JSON.

Make sure to check what the limits of this storage is. There is usually a max size for both JSON or strings depending on what the platform uses.

Database Storage
Not all of these platforms offer database APIs out-of-the-box like GameSparks does. This will make migrating RuntimeCollections or Game-Data collections more difficult.

In these cases you may need to resort to mapping your player data for those collections to JSON objects which can be stored using the platform's conventional player storage. This will mean revising how your existing GameSparks database queries are fetching data. If they are mostly querying by the playerId, this should be no problem, however, more complicated queries like playerId and itemId could be solved by storing the data as an array or times or, to make it easier to access data by Id, you can store them as an object containing multiple objects, as in the example below.

GameSparks Doc Example
The example below might be used in a “PlayerInventory” collection in GameSparks where you query using { “playerId” : “5c9208b4efe6a104f1c67e24”, “itemId” : 79 }

{
  "_id": {
    "$oid": "5c9208b4efe6a104f1c67e57"
  },
  "playerID": "5c9208b4efe6a104f1c67e24",
  "itemId": 79,
  "unlocked": false,
  "unique": true,
  "dateAdded": {
    "$date": {
      "$numberLong": "1553074356610"
    }
  },
  "lastUpdated": {
    "$date": {
      "$numberLong": "1553074356610"
    }
  }
}

To convert these docs to a single object you might use the following data-model.

"playerInventory" : {
  "8": {
    "itemId": 8,
    "unlocked": false,
    "unique": true
    ...
  },
  "79": {
    "itemId": 79,
    "unlocked": false,
    "unique": true
    ...
  },
  ...
}

This would allow you to replicate the query used in GameSparks to get the item by playerId and itemId by referencing the items by their Ids as playerInventory[itemId].

MetaCollections
As with player data, GameSparks MetaCollections make use of the database and return information using noSQL queries. This may not be an option for many alternative platforms as they have their own way of working with static game-data.

These platforms often call their equivalent to MetaCollections something else like title-data, content, game-data, etc, but they are work fundamentally the same; There is some way to add content, usually through the portal or its equivalent (REST API for complex data maybe), and an API client-side or in Cloud-Code to return the data so we can use it. It is also important that this content is cached in some way in order to boost performance.

You will encounter the same issues as you might expect with player-data. If the platform does not allow you to store simple JSON, you will need to create some custom objects so that your data can be serialized by the client.

In cases where the alternative platform offers some kind of SQL database you might have to think about modelling the data to a more strict format than your noSQL collections. This shouldn't be a problem, as MetaCollections are generally static, but you will have to consider how you are going to index fields when converting the data to a SQL table.

Other Features to Consider
What we listed in the section above were only the features we need in order to get similar functionality in the new platform as you are familiar with in GameSparks Cloud-Code. However, as you already know, there are a lot of GameSparks APIs that we didn't cover that may be important to your game.

In some cases, these features will just not be available on the destination platform. This might be a blocker to migration altogether, or there may be some workaround. Some examples of these cases are outlined below.

System-Scripts
System scripts cover a few different use-cases in GameSparks. Most GameSparks developers are familiar with the every-minute, every-hour and every-day scripts as they are commonly used. Other scripts like on-player connect/disconnect and on-publish are also used, however, we will not cover those here as they are specific to GameSparks and will not have a migration route, though they may be reproduced as custom-events or scripts.

For timed scripts (every-minute, etc) there are a few options. Some platforms come with these features themselves so it is only a case of porting the code (given the same complexities as porting any other Cloud-Code, it won't be as simple as copy-paste). In other cases we might have to use external services to run these scripts for us. We cover an example of this in the topic here here

Bulk-Jobs
Bulk-jobs are a tricky feature, even for GameSparks. With Bulk-jobs in GameSparks, the server will safely spread out the workload across all players involved and execute the job over time so as not to impact server performance. Bulk-jobs on other platforms therefore should not be just considered as a way just to execute code for every player in the game or a large sub-set of players, you should also consider server performance.

For GameSparks it is not advised to use Bulk-Jobs for every-day events, they are more intended for admin tasks, so if your existing Cloud-Code relies on Bulk-Jobs for every-day operations you should consider optimizing your code rather than trying to port over an inefficient feature.

Oftentimes, an alternative to Bulk-jobs is to trigger the update from a player-action. For example, if you need to inform players of an upcoming event, you can check for this upon login instead of changing something on every player’s account in order to flag their account for that event.

Something you can use in conjunction with the above suggestion is to return a list of active and inactive events when the player logs in. The inactive events come with a timestamp for when they become active and the active events come with a timestamp for when they end. This allows the client to track when updates are needed and double-check with the server when events should take place, therefore reducing the need for bulk-jobs to modify player accounts or send out bulk-messages to players to inform them of changes on the server, which is also a common use-case for bulk-jobs.

Schedulers
Most of these platforms will not have an alternative to the SparkScheduler API, but it may be possible to use something native to the programming language you are using such as SetInterval() for JS and C# or threading for Python.

It is important to remember that if you create your own alternative to these features of the Spark API, you're not replacing everything GameSparks does under-the-hood so it is important to consider the performance impact of these custom features.

Migrating MetaCollections
You will also need to consider how you are going to migrate your existing MetaCollections to the new platform’s alternative. As mentioned above, in most cases, these platforms do not have a database you can just move your meta-docs into. You will therefore need to work out how to model the data to the destination platform and you might have to consider automating this process through the use of the platform’s native REST APIs rather than copying the data over manually.

GameSparks API Wrappers
When it comes to migrating your existing GameSparks code it is natural to consider creating wrappers for certain common functionality between GameSparks and the destination platform.

There will certainly be cases where this will speed things up for you during migration. Saving data to SparkPlayer and calls to MetaCollections could be replaced with a wrapper API around the destination platform’s alternatives for example. This would make copy/pasting code faster as you would not have to rewrite every call to the database or player object, you could just paste the details into your new wrapper API functions.

Just remember, when approaching this work, that any inefficiencies in your existing code will also be migrated. So, while API wrapper will speed up migration, they will hide issues that might cause you performance problems later on.

Asynchronous APIs
GameSparks did not use an asynchronous approach for its APIs.

When you execute a request to the database, it will hold up your script and wait for a response before continuing to execute the rest of your code. All of your GameSparks calls are synchronous. This is going to be an issue when it comes to adapting your code for migration.

Some of these destination platforms do not use synchronous calls and instead use asynchronous calls.

If using asynchronous calls, when you send a request to your database or a 3rd party service (HTTP request for example), your code will not wait for the response before moving on to the next command.

Therefore, in a case like this in GameSparks…

var coinsBal = Spark.getPlayer().getBalance("COINS");
Spark.setScriptData("coins", coinsBal);

You will always have null or undefined returned to the client because the script will not wait for the response from the database.

There are several ways to overcome this problem which will be discussed in the sections below as they are specific to each platform’s programming language and APIs.

Regardless of that kind of adaptation you need to make to migrate your code, asynchronous and synchronous calls also have their uses separately, so don't just consider converting all your database wrappers to synchronous and continue porting your code. In some cases, like logging and updates, you don't always need a response from the database before continuing with your script. Using asynchronous requests in these cases will speed up the script execution time so consider them wherever you don't need to get a response from the database or cloud-store.

Performance Bottlenecks
It is extremely important to perform an internal review of your Cloud-Code before undertaking a migration and porting your code over to the new platform.

These platforms dont operate the same as GameSparks does so any database and player-data flows which are currently inefficient will also be migrated to the new platform. Any bottlenecks you currently have in code are likely to be ported along with your code, even if you create a wrapper for your GameSparks code in the new platform.

Limitations
As with GameSparks, Cloud-Code on these platforms often have limitations you will need to be aware of ahead of time.

These limits can be something you are familiar with from GameSparks, like execution times, but there can also be limits like script size (lines or Kbs), concurrent requests (how many of these requests can be in-process at one time), how many custom requests can you create per game, etc.

These are covered in each of the sections below per platform, so make sure to review any limitations before deciding if migration will work for your game. Porting the Cloud-Code may be a major undertaking, but it would be worse if the work is all completed only to find the new platform cannot handle your Cloud-Code at load.

Before we cover how AccelByte handles feature customization and Cloud-Code alternatives it is important to note that AccelByte is a product, and not a service like GameSparks. The key difference here is that when using AccelByte you are also engaging with the AccelByte team to ensure your game is set up and running correctly.

Rather than setting up an account and using the service through the SDK and portal like GameSparks or other alternatives, AccelByte will help you set up your own instances and create specific environments for your game and help you deploy it.

This does not mean that you cannot use their portal or their out-of-the-box features, it just means that you will need to engage with AccelByte before you can work with their tools.

If you wish to evaluate the product you can request a demo environment from AccelByte.

Throughout your engagement with AccelByte it is important to remember that you are working with the AccelByte team, not just a service provider. AccelByte can therefore assist your team with your migration from GameSparks in a number of ways.

Custom Microservices
If you need custom code or you need your code migrated you can engage with the AccelByte team directly and have them create a custom microservice for you. This will be deployed in the environments set up for you alongside the out-of-the-box microservices like Lobby, Leaderboards, etc.

AccelByte will work with your team to make sure the code is ported and optimized to meet their standards for scaling and performance.

Migration Support
If you choose to engage the AccelByte team for some custom work, they can also help you with player data migration and create custom tools integrated into your microservice to import your GameSparks player data into the new AccelByte instance.

Custom Game-Server
Another option for customizing AccelByte is to integrate their backend SDKs into your own service. These SDKs are available in JavaScript and Golang and offer an extended feature-set to AccelByte’s client SDKs. The idea behind these SDKs is that they allow you to create your custom features to your own requirements without the help of AccelByte. You can then host and deploy them yourself.

You can take a look at their repo here along with guides on how to get started.