How to Send & Receive Data

For the remaining integration steps, it is recommended that you have already developed a simple game template that requires a small amount of data to be exchanged. Otherwise, follow along by exchanging some small sample data. Here’s how you can send data using the SendData function:

Step 1: SendData

Here’s how you can send data using the SendData function.

Pass in your data to this function as a byte array (byte[]) to be sent to our Skillz servers which will be sent/forwarded to the opponent’s device.

Make sure to pass in all the data that’s needed to ensure that the game will stay in sync on both devices. The size limit is 2 KB.

For the data you send, we recommend having an enum type for each type of message you send so that you can easily handle the different types of data when it reaches the other device. JSON serialization will work, but is likely not the most performant way to send data. Here’s an example of how we send kicks back and forth in Soccer King Arena:

Note: The bytes passed to sendData above will be sent to the Sync game server and forwarded to your opponent’s onDidReceiveData implementation:

Recommended: For the data you send, have an object type for each type of message you send so that you can easily handle the different types of data when it reaches the other device. JSON serialization will work, but is likely not the most performant way to send data.

Recommended: Handle finishing states for the match in OnMatchCompleted and OnDidReceiveData.

Note: Reliability is only guaranteed up to a certain point when your opponent is disconnected. As soon as you receive notification that a player’s  opponent has been disconnected, you should stop attempting to send messages in order to ensure no data is lost. Also, if you find you are sending more than 30 messages a second, you may find that some messages could be lost on disconnect/reconnect and game state may need to be reconciled on reconnection.

Example:

public enum GameVariables
{
    SEND_SHOOT = 0,
    TIMED_OUT_ON_SHOT,
}

[Serializable]
public class GameEvent
{
    public GameVariables Type;
}

[Serializable]
public class ShootEvent : GameEvent
{
    public string Xvalue;
    public string Yvalue;
    public string UnitId;
    public string GameObjectsPositionsString;
    public string GoalBy;
}

public static void SendShootData(string Xvalue, string Yvalue, string UnitId, string gameObjectsPositionsString, string goalBy)
{
    ShootEvent shotEvent = new ShootEvent();
    shotEvent.Type = GameVariables.SEND_SHOOT;
    shotEvent.Xvalue = Xvalue;
    shotEvent.Yvalue = Yvalue;
    shotEvent.UnitId = UnitId;
    shotEvent.GoalBy = goalBy;
    shotEvent.GameObjectsPositionsString = gameObjectsPositionsString;
    SendData(shotEvent);
}

public static void SendData(GameEvent gameData)
{
    string json = JsonUtility.ToJson(gameData);
    byte[] rawData = Encoding.ASCII.GetBytes (json);
    SkillzCrossPlatform.SendData (rawData);
}

Step 2:OnDidReceiveData

Here is how you can receive data using the OnDidReceiveData function. This function needs to accept a byte array (byte[]) and is called whenever it receives data. We recommend adding in a switch statement in this function so you can handle each data differently depending on the type of data that came in.

Example:

public void OnDidReceiveData(byte[] value)
{
    string json = Encoding.ASCII.GetString(value);
    GameEvent data = JsonUtility.FromJson(json);

    switch (data.Type)
    {
       case GameVariables.SEND_SHOOT:
       {
          ShootEvent shootEventData = JsonUtility.FromJson(json);
          UnityMainThreadDispatcher.Instance().Enqueue(() => HandleIncomingShootDataChange(shootEventData));
          break;
       }
       case GameVariables.TIMED_OUT_ON_SHOT:
       {
          UnityMainThreadDispatcher.Instance().Enqueue(() => HandleIncomingTimerDataChange());
          break;
       }
    }
}

public void HandleIncomingShootDataChange(ShootEvent shootDataReceived)
{
    var unitId = shootDataReceived.UnitId;
    var xValue = float.Parse(shootDataReceived.Xvalue);
    var yValue = float.Parse(shootDataReceived.Yvalue);
    var gameObjectPositions = shootDataReceived.GameObjectsPositionsString;
    var goalBy = shootDataReceived.GoalBy;

    //do the physics calculations and shoot the ball
    Vector3 outPower = new Vector3(xValue, yValue, 0);

    if (GameSyncController.IsPlayerOne())
    {
        //handle player 2's changes since the current player is Player 1
       GlobalGameManager.setPendingKick(outPower, unitId.ToString(), "player2Team");
       globalGameManager.managePostShootSkillz("Player_2", gameObjectPositions, goalBy);
    }
    else
    {
        //handle player 1's changes since the current player is Player 2
        GlobalGameManager.setPendingKick(outPower, unitId.ToString(), "player2Team");
       globalGameManager.managePostShootSkillz("Player", gameObjectPositions, goalBy);
    }
}

public void HandleIncomingTimerDataChange()
{
    if (GameSyncController.IsPlayerOne())
    {
       globalGameManager.managePostTimerChangeSkillz(2);
       return;
    }
    else
    {
       globalGameManager.managePostTimerChangeSkillz(1);
       return;
    }
}

Next Step: Handle Game States