Skip to main content
Version: v3

Multiplayer Development Guide - C#

Introduction

Multiplayer is a C#-based game SDK that provides a complete client-side SDK solution for online games with strong networking requirements, thus eliminating the need for development teams to build their own servers and saving most of the development and maintenance costs. The main features provided by Multiplayer are as follows:

  • Getting room list
  • Creating a room
  • Joining a room
  • Randomly joining a (eligible) room
  • Getting players in a room
  • Getting, setting, and synchronizing room properties
  • Getting, setting, and synchronizing player properties
  • Sending and receiving custom events
  • Leaving a room

SDK Import

Please read Installation Guide to get the dll library files.

Initialisation

Import the required namespace.

using LeanCloud.Play;

Next we need to instantiate a client object for the Multiplayer SDK.

var client = new Client(
"your-client-id", // Client ID of the game
"your-client-token", // Client Token for the game
"tarara", // Set the user id
playServer: "https://your_server_url", // The game's API domain name
gameVersion: "0.0.1" // Set the game version; optional; default is 0.0.1; players with different versions will not be matched in the same room
);
  • The Client ID and Client Token of the game can be viewed at Developer Centre > Your Games > Game Services > Application Configuration.
  • The API domain name is viewable at Application Configuration > Domain Configuration > API. Refer to the documentation for domain name for more information.

Here userId serves as a unique identifier for the client connecting to the server. Note that this userId has the following restrictions:

  • Only letters, numbers, and underscores are allowed
  • Cannot exceed 32 characters in length
  • Globally unique within an application

gameVersion indicates the version number of the client, which can be used to route to different game servers if multiple versions of the game are allowed to co-exist.

Connections

Establishing a connection

Connect the current player to the Multiplayer service with the following code:

try {
await client.Connect();
// connection successful
} catch (PlayException e) {
// connection failure
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Lobby

We recommend that you don't add players to the lobby, because in the lobby, the server will constantly send out the latest full list of rooms, and players will choose one of the rooms to play by themselves, which is not only unfriendly to the player experience, but also brings a lot of bandwidth pressure. We recommend you to use Room Matching like most of the mobile games nowadays to quickly match players and let them start the game.

If you have a special game scenario where you need to get the room list, you can call the following method:

try {
await client.JoinLobby();
// Joined the lobby successfully
} catch (PlayException e) {
// Failed to join the lobby
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

When a player joins the lobby, the server sends the list of rooms in the current lobby to the client, and the developer can view the list of rooms or join a room to join the game as needed.

client.OnLobbyRoomListUpdated += () => {
var roomList = client.LobbyRoomList;
// TODO can do the logic for displaying the list of rooms.
};

For more on LobbyRoom, see API documentation.

EventParameterDescription
OnLobbyRoomListUpdatedNoneLobby Room List Updated

Room Matching

A room is a unit that generates "combat interactions" between players. For example, the card table of Landlord, the copy of MMO, the battle of WeChat game, etc., all belong to the category of room in a broad sense. The combat interactions between players are all done in the room. Therefore, how players enter a room is the key to room matching. Below we will analyse the commonly used "Room Matching" function from the aspects of "Create Room" and "Join Room".

Creating a Room

We can create a room simply like this. The player who creates the room is the room owner (MasterClient), and when the owner creates a room successfully, he/she also joins the room.

try {
await client.CreateRoom();
// Successful room creation also means that you have successfully joined the room.
} catch (PlayException e) {
// Failed to create room
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

We can also create a room that sets the relevant information.

// Custom properties of the room
var props = new PlayObject {
{ "title", "room title" },
{ "level", 2 }
};
var roomOptions = new RoomOptions {
Visible = false,
EmptyRoomTtl = 10000,
PlayerTtl = 600,
MaxPlayerCount = 2,
CustomRoomProperties = props,
CustoRoomPropertyKeysForLobby = new List<string> { "level" },
Flag = CreateRoomFlag.MasterSetMaster | CreateRoomFlag.MasterUpdateRoomProperties,
};
var expectedUserIds = new List<string> { "cr3_2" };
try {
await client.CreateRoom(roomName, roomOptions, expectedUserIds);
// Successful room creation also means that you have successfully joined the room.
} catch (PlayException e) {
// Failed to create room
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

The parameters roomName, roomOptions and expectedUserIds are optional.

roomName

The room name must be unique; if it is not set, the server generates a unique room id and returns it.

roomOptions

Specified parameters when creating a room, including:

  • Opened: whether the room is open or not. If set to false, no other players are allowed to join.
  • Visible: whether the room is visible. If set to false, it will not appear in the lobby's room list, but other players can join the room by specifying the room name.
  • EmptyRoomTtl: how long (in seconds) the room will be kept when there are no players in the room. Default is 0, i.e. the room data will be destroyed immediately when there are no players in the room. Maximum value is 300, i.e. 5 minutes.
  • PlayerTtl: the time (in seconds) to keep the player's data in the room when the player drops out. Default is 0, i.e. player data is destroyed immediately after the player drops out. Maximum value is 300, i.e. 5 minutes.
  • MaxPlayerCount: the maximum number of players allowed in the room.
  • CustomRoomProperties: custom properties for the room.
  • CustomRoomPropertyKeysForLobby: an array of keys in CustomRoomProperties. The properties contained in CustomRoomPropertyKeysForLobby will appear in the lobby's room Properties (client.LobbyRoomList), and the full set of properties can be viewed in room.CustomProperties after joining the room. These properties will be used when matching rooms.
  • Flag: flag bit for room creation. See MasterClient drop without transfer, Specify other member as MasterClient, and Allow only MasterClient to modify room properties below for more details.

expectedUserIds

An array of specified player IDs. This parameter is mainly used to "reserve space" for certain players who can join the room.

Note: These "specific players" will not actually join the room. There will only be space in the room reserved for those "specific players" to join. If you want to invite players to join a room, you need to send the room name to your friends through other channels, such as IM, WeChat, etc., and then your friends will join the room through the JoinRoom(roomName) interface.

For more information about CreateRoom, please refer to API Documentation.

Joining a room

When a room is created, other players can participate in the game by "joining the room".

Join the designated room

You can join a room by specifying the room name.

try {
// The player is joining the 'LiLeiRoom' room.
await client.JoinRoom("LiLeiRoom");
// Join the room successfully
} catch (PlayException e) {
// Failed to join room
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

When joining a room, you can also take up space for other players. If the remaining empty space in the room is less than the number of occupied spaces, you will fail to join the room.

var expectedUserIds = new List<string>() { "LiLei", "Jim" };
try {
// Players are joining the "game" room and taking up space for LiLei and Jim
await client.JoinRoom("game", expectedUserIds);
} catch (PlayException e) {
// Failed to join room
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

For more on JoinRoom, see the API documentation.

Randomly joining rooms

Sometimes, instead of joining a specific room, we can randomly join "rooms that meet certain conditions" (or even no conditions), such as quick start, quick match, etc. In this case, we can randomly join rooms by calling the JoinRandomRoom method.

try {
await client.JoinRandomRoom();
// Join the room successfully
} catch (PlayException e) {
// Failed to join room
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

It is also possible to set "conditions" for random joining, such as randomly joining a room with level = 2.

// Setting Matching Properties
var matchProps = new PlayObject {
{ "level", 2 }
};
try {
await client.JoinRandomRoom(matchProps);
} catch (PlayException e) {
// Failed to join room
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Join or create a specific room

Many games don't have scenarios that force a designated room owner. As long as a number of players can play in the same room, it is fine for anyone to be the owner. We can use JoinOrCreateRoom() to achieve this. This method will join the current player directly to the room if there is a relevant room, or create a new room if there isn't such a room.

try {
// For example, if 4 players join a room with the name "room1" at the same time, if it doesn't exist, then create and join the room with the name "room1".
await client.JoinOrCreateRoom("room1");
} catch (PlayException e) {
// Failed to join a room and did not successfully create a room
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

For more on JoinOrCreateRoom, see the API documentation.

New player joining event

For players who are already in the room, when a new player joins the room, the server will send the OnPlayerRoomJoined event to notify the client, and the client can use the properties of the new player to do some display logic.

// Register the event for new players joining
client.OnPlayerRoomJoined += newPlayer => {
// TODO New Player Joining Logic
};

You can get all the players in the room by client.Room.PlayerList.

Determining local players

Once you have all the players in the room, you can determine if a Player is the current local player.

var players = client.Room.PlayerList;
var player = players[0];
var isLocal = player.isLocal;

Leaving a room

The following interface can be called when the player wants to proactively leave the room.

try {
await client.LeaveRoom();
// Leaving the room successfully; you can do things like going to another scene
} catch (PlayException e) {
// Failed to leave the room
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Other players in the room will receive the OnPlayerRoomLeft event.

// Register the event of a player leaving the room
client.OnPlayerRoomLeft += leftPlayer => {
// TODO can perform the destruction operation for the player's departure
}
EventParameterDescription
OnPlayerRoomJoinednewPlayerA new player joined the room
OnPlayerRoomLeftleftPlayerA player left the room

MasterClient

The room creator will become the MasterClient by default in the Multiplayer service. They can close the room, set the room to be invisible, kick people, and so on. Their device is also responsible for "logical operations". For example, it can be responsible for the following scenarios in the game:

  • Dealing cards for users in card games;
  • Controlling the timing and logic of killing monsters;
  • Judging the winner at the end of the game;
  • ...and so on.

MasterClient located on the player's client

You can write the MasterClient logic in the client, and the player terminal ofthe room creator will take care of the game operations. In this case, Multiplayer provide the following convenient features for MasterClient:

MasterClient Drop Transfer

When a MasterClient is dropped, the Multiplayer service assigns a new player client as the MasterClient. Even if the original MasterClient comes back online, it does not become the new MasterClient. When the MasterClient is changed, the SDK dispatches the OnMasterSwitched event to notify the client when the MasterClient changes.

// Register the host switch event
client.OnMasterSwitched += newMaster => {
// TODO can display that the host is changed

// You can determine whether to perform logical processing according to whether the current client is the Master.
if (client.Player.IsMaster) {

}
}
EventParameterDescription
OnMasterSwitchednewMasterMaster changed

Specify other members as MasterClient

During the game, if the MasterClient does not want to take the computing function anymore, it can call the following method to actively transfer its role to other player clients:

try {
// Specify MasterClient by Player Id
await client.SetMaster(newMasterId);
} catch (PlayException e) {
// Failed to set Master
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

After transferring the MasterClient identity, all players in the room will receive the OnMasterSwitched event.

MasterClient on the server

In order to ensure game security and prevent users from cheating, we recommend that you host your MasterClient in the Client Engine provided by TDS and configure the following permissions:

MasterClient will not be transferred when it drops

After placing the MasterClient on the server, the MasterClient role should not be transferred even if it unexpectedly drops out of the game, in which case you need to specify the FixedMaster flag when creating a room:

var options = new RoomOptions {
Flag = CreateRoomFlag.FixedMaster
};
await client.CreateRoom(roomOptions: options);

MasterClient operation

Set whether a room is open or not

MasterClient can set whether a room is open or not, so that other players are not allowed to join when the room is closed.

try {
// Setting the room to close
await client.SetRoomOpen(false);
Debug.Log(client.Room.Open);
} catch (PlayException e) {
// Failed to set
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Setting whether a room is visible or not

MasterClient can set whether a room is visible or not. When the room is not visible, this room will not appear in the player's lobby room list, but other players can join by specifying roomName.

try {
// Setting the room invisible
await client.SetRoomVisible(false);
Debug.Log(client.Room.Visible);
} catch (PlayException e) {
// Failed to set
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Kicking people

MasterClient can kick other players in a room out of the room:

try {
// You can pass in the code and msg of the kicker.
await client.KickPlayer(otherPlayer.ActorId, 1, "You've been kicked out of your room.");
} catch (PlayException e) {
// Failed to kick
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

After being kicked out of a room, the kicked player receives the OnRoomKicked event.

client.OnRoomKicked += (code, msg) => {
// code and msg are what the MasterClient passes when it kicks someone.
};

At the same time, players other than the MasterClient that are still in the room will receive the OnPlayerRoomLeft event:

client.OnPlayerRoomLeft += leftPlayer => {

};

Custom Attributes and Synchronisation

In order to meet the different gameplay needs of developers, the Multiplayer SDK allows developers to set custom properties.

The main functions of custom property synchronization include:

  • Keeping the data consistent among multiple clients.
  • Custom attributes are managed by the server. When a player enters a room, all custom attributes will be available.

Custom attributes are divided into "Room Custom Attributes" and "Player Custom Attributes".

Room Custom Attributes

You can set a PlayObject type custom attribute for a room, such as the number of rounds in a battle, all the pieces in a room, and so on.

// Set the custom properties you want to modify
var props = new PlayObject {
{ "gold", 1000 }
};
try {
// Set the gold property to 1000
await client.Room.SetCustomProperties(props);
var newProperties = client.Room.CustomProperties;
} catch (PlayException e) {
// Failed to set the property
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Note: This interface does not directly set the memory values of custom properties in the client, but sends a message to modify the custom properties, and the server makes the final decision whether to change them or not.

When a room property is changed, the SDK will dispatch the OnRoomCustomPropertiesChanged event to notify all player clients (including itself).

// Registering room property change events
client.OnRoomCustomPropertiesChanged += changedProps => {
var props = client.Room.CustomProperties;
var gold = props.GetInt("gold");
};

Note: The changedProps parameter only indicates the currently changed parameters, not all properties. To get all properties, please get them via client.Room.CustomProperties.

Allow only MasterClient to modify room properties

By default, all clients in a room can modify the room's custom properties. If you want to allow only the MasterClient to do so, you can specify the MasterUpdateRoomProperties flag when creating the room:

var options = new RoomOptions {
Flag = MasterUpdateRoomProperties
};
await client.CreateRoom(roomOptions: options);

Player custom attributes

Player custom attributes are essentially the same as room custom attributes.

// Poker Objects
var poker = new PlayObject {
{ "flower", 1 },
{ "num", 13 }
};
var props = new PlayObject {
{ "nickname", "Li Lei" },
{ "gold", 1000 },
{ "poker", poker }
};
try {
// Request to set player attributes
await client.Player.SetCustomProperties(props);
} catch (PlayException e) {
// Failed to set the property
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Any player in the room (including yourself) who modifies their custom attributes triggers the Player Custom Attribute Change event:

// Register player custom attribute change events
client.OnPlayerCustomPropertiesChanged += (player, changedProps) => {
// Get all the player's custom attributes
var props = player.CustomProperties;
var title = props.GetString("title");
var gold = props.GetInt("gold");
};

CAS

CAS stands for Compare And Swap, which means check and update, and is used to avoid some concurrency problems.

When SetCustomProperties is called, the server receives all the values submitted by the client, which is not enough for some scenarios.

For example, a property holds the holder of an item in this room, i.e. the key of the property is the item and the value is the holder. Any client can set this property at any time, and if multiple clients call it at the same time, the server will take the last call received as the final value, which is usually illogical. Usually the item should belong to the first person who got it.

SetCustomProperties has an optional parameter expectedValues that can be used as a judgement condition. The server will only update properties that currently match expectedValues. Updates with expired expectedValues will be ignored.

Suppose there are 10 players in a room, but there is only 1 Tulong Knife, and the Tulong Knife can only be won by the first person who "grabs it".

We can set the tulong value in expectedValues:

var props = new PlayObject {
// X indicates the current client
{ "tulong", X }
};
var expectedValues = new PlayObject {
// Current owner of the Tulong Knife
{ "tulong", null }
};
await client.Room.SetCustomProperties(props, expectedValues);

This way, after the first player gets the Tulong Knife, tulong corresponds to the value of first player, and subsequent requests with expectedValues = { tulong: null } will fail.

EventsParametersDescription
OnRoomCustomPropertiesChangedchangedPropsRoom custom property is changed
OnPlayerCustomPropertiesChanged(player, changedProps)Player custom property is changed

Custom events

In Custom Properties, we introduced how to customize the game's data structures and data types based on the game's requirements. However, data by itself is not enough. To allow different parties to communicate, custom events are also needed.

Sending custom events

We can send various events through custom events, such as game start, card capture, release X skill, game end, etc.

var options = new SendEventOptions {
// Set the receiving group of the event to Master
ReceiverGroup = ReceiverGroup.MasterClient
// You can also specify the receiver actorId
// TargetActorIds = new List<int>() { 1 },
};
// Setting Skill Id
var eventData = new PlayObject {
{ "skillId", 123 }
};
// Setting the skill event id
byte SKILL_EVENT_ID = 1;
try {
// Send custom events
await client.SendEvent(SKILL_EVENT_ID, eventData, options);
} catch (PlayException e) {
// Failed to send the event
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

The options are the event sending parameters, including the receiving group and the receiver ID array.

  • ReceiverGroup is an enumeration of the targets of the received event, including Others (everyone in the room except yourself), All (everyone in the room), and MasterClient (the host).
  • The Receiver ID array is the enumerated value of the target of the received event, i.e., the player's ActorId array. The ActorId can be obtained via player.ActorId.

Note: If both receiver group and receiver ID array are set, receiver ID array will override receiver group.

Receiving custom events

When the event is sent successfully, the custom event OnCustomEvent in the event receiver will be triggered, and different events can be handled according to the eventId (event ID).

// Registering custom events
client.OnCustomEvent += (eventId, eventData, senderId) => {
if (eventId == SKILL_EVENT_ID) {
// If it is a skill event
var skillId = eventData.GetString("skillId");
// TODO Handling the display of released skills

}
};

event Parameters

parametertypedescription
eventIdbyteEvent Id for the event
eventDataPlayObjectEvent parameter
senderIdintEvent sender ID (player's actorId)

Disconnect

In case of unstable network, you may be disconnected passively. When you are disconnected passively, the SDK will send OnDisconnected event to the client, where the developer can alert the player on the UI:

// Registering for disconnection events
client.OnDisconnected += () => {
// TODO Reconnect if needed
};

Disconnect and reconnect

Keep disconnected users in the room.

Players may get disconnected when there's unstable network or when they put the game in the background. When the player drops out, the OnPlayerActivityChanged event will be triggered for other online players:

// Register Player Dropping/Connecting Events
client.OnPlayerActivityChanged += player => {
// Get the status of whether a user is "active" or not
Debug.Log(player.IsActive);
// TODO You can update display and run other logic according to the player's online status.
};

If a player doesn't come back to the game for a long time, the server will remove the player from the room and destroy the player's data, and the other players in the room will receive the OnPlayerRoomLeft event. When creating a room, we can set the timeout via PlayerTtl, so that when a player drops out of the room, the server will keep the data of the dropped player in the room during the PlayerTtl time and wait for the player to come back online.

var options = new RoomOptions {
// Set PlayerTtl to 300 seconds
PlayerTtl = 300
}
await client.CreateRoom(roomOptions: options);

When a dropped player returns to the room within the PlayerTtl time, the other player's OnPlayerActivityChanged event will be triggered again, and the developer can determine the player's current state in this event.

Reconnect

Users can reconnect to the server after being disconnected through the following interface.

client.OnDisconnected += async () => {
// Reconnect
await client.Reconnect();
};

Note: This interface will only reconnect you to the server, not return you directly to the room if you were previously playing in the room.

Recommended Reconnect and get room, then the player can choose whether or not to return to the room.

You can also just Reconnect and return to room.

Reconnect and get room

After reconnecting, the player can use the FetchMyRoom interface to fetch the room they were in before leaving:

  • If the player is still in the room, the room ID will be returned and the game will inform the player of this state. If the player chooses to return to the room they were in before leaving, they can call the RejoinRoom interface.
  • If the player is not in the room, or the room has been destroyed, a 4301 exception will be returned, and the game can handle it accordingly.
string roomName = await client.FetchMyRoom();

Rejoin a room

When a player connects to the server, they can rejoin a room via the Rejoin interface. If the player calls this interface within the PlayerTtl time limit, the player will be able to rejoin the room successfully; if the PlayerTtl time limit is exceeded, rejoining the room will fail.

try {
// reconnect
await client.Reconnect();
// The reconnection was successful. We're back in the same room as before.
if (roomName) {
try {
await client.RejoinRoom(roomName);
// If you return to the room successfully, the other players in the room will receive the `OnPlayerActivityChanged` event.
} catch (PlayException e) {
// Failed to return to room
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}
}
} catch (PlayException e) {
// reconnect failure
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Reconnect and return to the room

This interface is the equivalent of Reconnect() and RejoinRoom() combined. With this interface, you can directly reconnect and go back to the previous room.

try {
await client.ReconnectAndRejoin();
// Successfully return to the room; update data and interface
} catch (PlayException e) {
// Failure to reconnect or return
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Close

Developers can also proactively close the SDK via the following interface, after which the Client needs to be re-instantiated if it needs to be used again.

client.Close();

Error handling

Specific exception messages can be caught with try-catch when we initiate a request. For example, when creating a room:

try {
await client.createRoom();
// Room Creation Successful
} catch (PlayException e) {
Debug.LogErrorFormat("{0}, {1}", e.Code, e.Detail);
}

Critical error event

If a major error is currently occurring, this event will be triggered. It won't be triggered easily, and if it is triggered, please contact technical support to deal with it.

client.OnError += (code, detail) => {
// Contact Technical Support

};

Serialisation

In the new version of Play, we provide richer ways to synchronise data. The main ones include container types (PlayObject/PlayArray) and custom types.

PlayObject

PlayObject is a replacement for the Dictionary<string, object> type in older versions.

PlayObject implements the IDictionary<object, object> interface, which provides a more convenient fetch interface in addition to the IDictionary interface.

Common interfaces are listed below:

// basic type
public bool GetBool(object key);
public int GetInt(object key);
public float GetFloat(object key);
...
// Container type
public PlayObject GetPlayObject(object key); // PlayObject supports nesting
public PlayArray GetPlayArray(object key);
public T Get<T>(object key);

For more interfaces, please refer to

PlayArray

PlayArray implements the IList interface and is mainly used for synchronisation of array objects, similar to PlayObject.

Commonly used interfaces are as follows:

// basic type
public bool GetBool(int index);
public int GetInt(int index);
public float GetFloat(int index);
...
// Container type
public PlayObject GetPlayObject(int index);
public PlayArray GetPlayArray(int index);
public T Get<T>(int index);
// conversion interface
public List<T> ToList<T>();

For more interfaces, please refer to

Custom types

In addition to supporting the two container types mentioned above, Play also supports synchronising data of Custom Types.

Suppose we have a Hero type with id, name, and hp defined as follows:

class Hero {
int id;
string name;
int hp;
}

To synchronise data of type Hero, the following two steps are required:

Implement serialisation / deserialisation methods

The implementation of serialisation methods is up to the developer. You can use protobuf, thrift, etc. It is sufficient if the serialisation and deserialisation interfaces supported by Play are met.

public delegate byte[] SerializeMethod(object obj);
public delegate object DeserializeMethod(byte[] bytes);

The following is an example of the way to serialise a PlayObject provided by Play.

// Serialisation methods
public static byte[] Serialize(object obj) {
Hero hero = obj as Hero;
var playObject = new PlayObject {
{ "id", hero.id },
{ "name", hero.name },
{ "hp", hero.hp },
};
return CodecUtils.SerializePlayObject(playObject);
}
// deserialisation method
public static object Deserialize(byte[] bytes) {
var playObject = CodecUtils.DeserializePlayObject(bytes);
Hero hero = new Hero {
id = playObject.GetInt("id"),
name = playObject.GetString("name"),
hp = playObject.GetInt("hp"),
};
return hero;
}

Registering Custom Types

When implementing a serialisation method, remember to register the custom type before using it.

CodecUtils.RegisterType(typeof(Hero), typeCode, Hero.Serialize, Hero.Deserialize);

where typeCode is a numeric code representing the custom type, which is used to determine the custom type during deserialisation.

API Documentation

For more interfaces and details, please refer to API Interfaces.