1. Home
  2. Docs
  3. UniREST Solution 3.5
  4. UniREST Client
  5. Special Features
  6. SyncTable

SyncTable

What it is

A SyncTable (synchronized table) is a special database table with a fixed structure, managed by server and client built-in functionalities. The scope of this table and the features around it is to create a simple but efficient way for developing multiplayer games or, in general, systems that can be used by different users at the same time, sharing the same experience (e.g. a chat).


Table structure

Each record of a SyncTable is designated to a specific user. For this reason, the first piece of information in the record is the user id; the same id that the user has in the users table. Therefore, a record can be seen as a user’s data storage space.

The part of the record for storing the data consists of 10 Slots of string type (in the table, they are 10 columns of TEXT type).

The last element of a record is a Token. It is a string of random letters that is generated when data is written and which, consequently, changes only when new data is written. It is used by UniREST Server to understand if the record has changed (and send the data to the Client) or not (in this case, it doesn’t have to do anything).

A SyncTable record

| user ID | 10 Slots for data storage | Token |


How it works

On the server-side, the only thing you have to do is just to create one (or more) SyncTable (see the Create a SyncTable chapter). There’s no need to do something else; no APIs or other server settings. Most of the job is on the client-side.

The SyncTable functionalities are into the UniRESTClient.SyncTable class. This is a lightweight, pretty easy-to-use class, which contains only what is strictly necessary to function.

The general system logic is quite basic:

  • the client constantly communicates with a SyncTable asking for its updated data;
  • the server sends the SyncTable data only when it changes. This means that if the data in the SyncTable doesn’t change, the server doesn’t respond;
  • as soon as a write to the SyncTable occurs, the server sends the new data to the client and the control cycle continues.

Users ID

In a multiplayer/multiuser logic, there is a place (a room) where all the users are and in which each user can communicate with each other and see other users’ data. This means, that in a Unity project you will have your ID (it simply comes from the login session) and the IDs of the other users you want to play or communicate with.

It is up to you to develop the system to obtain those IDs.

The user ID is a fundamental element because the reading and writing of data take place on the record associated with that id.


SyncTable C# development

In Unity, to use the SyncTable feature you have to use the UniRESTClient.SyncTable class. Remember that it works under the async programming logic, so if required you must use async methods from the UniRESTClient.Async class.


1. SyncTable instance

The first thing to do is to declare a new instance of the UniRESTClient.SyncTable class. This will initialize the SyncTable engine. Note that this instance is not intended to be used with one table only. It is an engine, so it can be used to manage several different tables.

var multiplayer = new UniRESTClient.AsyncTable();

2. Adding profiles

A profile defines the basic configuration for communicating with a SyncTable: the name of the table (the name chosen in the UniREST Server application) and the ID of the user whose data is to be detected. Finally, a unique ID (a free choice string) to identify this profile.

To define a profile, use the Add() method of the instance.

multiplayer.Add(id, tableName, userID, millisecondsDelay = 1000);
idstringA string representing a unique ID for this profile.
tableNamestringThe name of the SyncTable to read/write.
userIDintThe ID of the user whose SyncTable’s record has to be read/written.
millisecondsDelayint
(optional)
Default: 1000 (1 second)
How often (in milliseconds) the SyncTable must be contacted to detect user data.
// Instance declaration.
var multiplayer = new UniRESTClient.SyncTable();

// Adding some profiles.
multiplayer.Add("me", "my_synctable", UniRESTClient.UserID, 2000);
multiplayer.Add("opponent01", "my_synctable", player01ID);
multiplayer.Add("opponent02", "my_synctable", player02ID);

3. Read callbacks for each profile

Now it’s time to decide what to do when a user’s data changes in a SyncTable. When the UniREST Server intercept a change in the user’s record, the Read() method is called and the callback defined in this method is executed. The user’s data is available in the UniRESTClient.SyncTable.Data parameter you must provide in the callback definition.

multiplayer.Read(id, callBack);
idstringA string representing a unique ID of a profile.
callBackAction<UniRESTClient.SyncTable.Data>The function to call when the user’s data (the user of the given profile) is received.
The function must have a parameter of the UniRESTClient.SyncTable.Data type.
// Instance declaration.
var multiplayer = new UniRESTClient.SyncTable();

// Adding some profiles.
multiplayer.Add("me", "my_synctable", UniRESTClient.UserID, 2000);
multiplayer.Add("opponent01", "my_synctable", player01ID);
multiplayer.Add("opponent02", "my_synctable", player02ID);

// Defining the Read callbacks.
multiplayer.Read("me", (UniRESTClient.SyncTable.Data data) => {
   // Here what to do with my data...
});
multiplayer.Read("opponent01", (UniRESTClient.SyncTable.Data data) => {
   // Here what to do with "opponent01" received data...
});
multiplayer.Read("opponent02", (UniRESTClient.SyncTable.Data data) => {
   // Here what to do with "opponent02" received data...
});

4. Start listening

Once everything has been defined, you have to start the SyncTable engine. To start the engine means to start reading all the defined tables and users’ data. Basically, it equals listening to what users are doing.

To start the engine you have to use the ListenToAll() method (for reading all the profiles) or the ListenTo(id) method (for reading a specific profile).

multiplayer.ListenToAll();
multiplayer.ListenTo(id);
idstringA string representing a unique ID of a profile.
// Instance declaration.
var multiplayer = new UniRESTClient.SyncTable();

// Adding some profiles.
multiplayer.Add("me", "my_synctable", UniRESTClient.UserID, 2000);
multiplayer.Add("opponent01", "my_synctable", player01ID);
multiplayer.Add("opponent02", "my_synctable", player02ID);

// Defining the Read callbacks.
multiplayer.Read("me", (UniRESTClient.SyncTable.Data data) => {
   // Here what to do with my data...
});
multiplayer.Read("opponent01", (UniRESTClient.SyncTable.Data data) => {
   // Here what to do with "opponent01" received data...
});
multiplayer.Read("opponent02", (UniRESTClient.SyncTable.Data data) => {
   // Here what to do with "opponent02" received data...
});

// Start the listening to all this callbacks.
multiplayer.ListenToAll();
// Start the listening to all this callbacks.
multiplayer.ListenTo("me");
multiplayer.ListenTo("opponent01");
multiplayer.ListenTo("opponent02");

UniRESTClient.SyncTable.Data class

The UniRESTClient.SyncTable.Data class represents a package of user’s data that can be read or written in a SyncTable. It exposes 10 string properties, one for each Slot you can read or write. These properties have names starting with s and a number from 1 to 10.

When a property is null, means that the Slot is empty (during the reading operation) or the data mustn’t be written (during the writing operation).

Other class properties must be ignored because are reserved for the engine functioning.

var myData = new UniRESTClient.SyncTable.Data();

myData.s1 = "Conan";
myData.s4 = "Sword";
myData.s5 = "1000";

Writing data

While you should use one Read() method for each user whose data you want to read, the write() method is intended to be used only by the user who is playing or communicating. This is because, typically, each user writes their own data and not the data of other users (however, a user can modify another user’s data simply by using the Write() method on that user’s profile).

To write a user’s data you have to use the Write() async method. It requires the profile ID (so as to know which user’s data to modify and in which table) and a package of data to send for writing. There is a callback parameter to catch when the writing operations are completed, but it can be null if not required.

multiplayer.Write(id, data, callBack);
idstringA string representing a unique ID of a profile.
dataUniRESTClient.SyncTable.DataThe data to write into the SyncTable’s Slots.
callBackAction<bool>A function to call when the writing operation is completed.
It can be null if not required, otherwise, it must be declared with a bool parameter, that will be true if operations succeeded, false in case of error.

When you perform writing on a SyncTable, the Read() method is called so as to allow you to update your Unity project data.

var myData = new UniRESTClient.SyncTable.Data();
myData.s1 = "Conan";
myData.s4 = "Sword";
myData.s5 = "1000";

_ = multiplayer.Write("me", myData, null);
_ = multiplayer.Write("me", myData, (bool ok) => {
    if (ok) Debug.Log("Writing done!"); else Debug.LogError("Writing failed!");
});

Stop Listening

When you start the SyncTable engine, it continues to work without stopping. At a certain moment, you may need to stop the Read() methods (e.g. when you change Scene). In this case, you have to call the StopAllListening() method (for stopping all the active reading precesses) or the StopListening(id) method (for stopping a specific profile).

multiplayer.StopAllListening();
multiplayer.StopListening(id);
idstringA string representing a unique ID of a profile.

Tips

What to save into a Slot

Having just 10 Slots of string data may look limiting. However, this doesn’t mean you can manage 10 strings only. For example, you can use the UniREST Json class to store more data in a single slot. Or use the UniRESTBinary class for storing complex packages of data.

// More values into a single JSON structure.
var myJData = new Json();
myJData.Add("player", "Conan");
myJData.Add("health", 100);
myJData.Add("strength", 10.6);
myJData.Add("hasWeapon", true);

// Adding this JSON to a SyncTable Data package.
var myData = new UniRESTClient.SyncTable.Data();
myData.s1 = myJData.ToString();

// Writing.
_ = multiplayer.Write("me", myData, null);

About Multiplayer Games

If you are thinking to use the SyncTable feature to develop a multiplayer game, keep in mind that the UniREST Solution communication system relies on the HTTP protocol.

The HTTP is a kind of “one-shot” communication: the client asks something to the server, the server replies and the client is able to read that reply. Then the communication is closed and the server has no way to contact again the client. If on the server-side there is something that the client needs, it’s the client’s job to contact the server at the right time and ask for what it needs.

This approach is totally different compared to a TCP communication: in this case, the client contacts the server and from that moment client and server are under a constantly opened communication channel. The client can contact the server at any moment, and the server can contact the client at any moment. Moreover, it’s direct communication.

For this reason, you must consider carefully what kind of multiplayer system you need. For example, take into consideration the time it takes for the client to receive a response from the server. In part, it is affected by the millisecondsdelay parameter of the Add() method, but then there is the time to contact the server and the time the server has to process and send the response.

So, if you need to develop a system like a chat or a simple multiplayer game where speed response can happen in terms of one or two seconds, the SyncTable can be a good solution. Instead, if you are going to develop a first-person arena shooter, you should consider TCP or similar technology with all the efforts (and costs) it requires.