Developer Documentation

Understanding User Data

Every peer (players or user connected to an ODIN room) has its own user data. User data within ODIN is always defined like this:

public byte[] UserData { get; set; }

As you can see, it’s just an array of bytes. How you choose to use this is entirely up to you. For example, in simpler games like casual games, board games, or other multiplayer games that don’t require advanced features like movement prediction, you don’t need complex multiplayer frameworks like Mirror Networking or Unity MLAPI to build your game. ODIN’s user data is typically enough, as you can link your custom scripts to the OnPeerUserDataChanged callback, which is triggered whenever user data changes for a connected peer.

If you’re planning to use or are already using one of these more complex frameworks, you’ll need to sync ODIN with those tools, as your players will be connecting to two different servers: the (dedicated) server for the game and ODIN’s backend servers for voice. But don’t worry, it’s simpler than it seems—ODIN’s SDK handles all the heavy lifting for you. In your multiplayer game, players might be on different teams, and only those teams should share the same ODIN room. To synchronize this information between ODIN and your game, you can use UserData.

In our examples, we use a simple class that defines a straightforward structure, serializes it to a JSON string, and sets it as the peer’s user data. Then, on the receiving end, this string is deserialized back into a class instance.

You’ll find this class in the samples folder of the Unity SDK, and it looks like this:

CustomUserDataJsonFormat implementation
using OdinNative.Odin;
using System;
using System.Text;
using UnityEngine;

namespace OdinNative.Unity.Samples
{
    [Serializable]
    class CustomUserDataJsonFormat : IUserData
    {
        public string name;
        public string seed;
        public string status;
        public int muted;
        public string user;
        public string renderer;
        public string platform;
        public string revision;
        public string version;
        public string buildno;

        public CustomUserDataJsonFormat() : this("Unity Player", "online") { }
        public CustomUserDataJsonFormat(string name, string status)
        {
            this.name = name;
            this.seed = SystemInfo.deviceUniqueIdentifier;
            this.status = status;
            this.muted = 0;
            this.user = string.Format("{0}.{1}", Application.companyName, Application.productName);
            this.renderer = Application.unityVersion;
            this.platform = string.Format("{0}/{1}", Application.platform, Application.unityVersion);
            this.revision = "0";
            this.version = Application.version;
            this.buildno = Application.buildGUID;
        }

        public UserData ToUserData()
        {
            return new UserData(this.ToJson());
        }

        public static CustomUserDataJsonFormat FromUserData(UserData userData)
        {
            return JsonUtility.FromJson<CustomUserDataJsonFormat>(userData.ToString());
        }

        public static bool FromUserData(UserData userData, out CustomUserDataJsonFormat customUserData)
        {
            try
            {
                customUserData = JsonUtility.FromJson<CustomUserDataJsonFormat>(userData.ToString());
                return true;
            }
            catch (Exception e)
            {
                Debug.LogException(e);
                customUserData = null;
                return false;
            }
        }

        internal string GetAvatarUrl()
        {
            return string.Format("https://avatars.dicebear.com/api/bottts/{0}.svg?textureChance=0", this.seed);
        }

        public string ToJson()
        {
            return JsonUtility.ToJson(this);
        }

        public override string ToString()
        {
            return this.ToJson();
        }

        public byte[] ToBytes()
        {
            return Encoding.UTF8.GetBytes(this.ToString());
        }
    }
}

If you like this way of handling user data, copy and paste the code into your Unity project, rename it to whatever suits you (and update the namespace). Then, use it to set the user data when joining a room.

Using our CustomUserDataJsonFormat
// Generate a JSON User Data object with the players netId and name in the network
CustomUserDataJsonFormat userData = new CustomUserDataJsonFormat(name, "online");
userData.seed = netId.ToString();

// Join ODIN Room ShooterSample and send user data
OdinHandler.Instance.JoinRoom("ShooterSample", userData.ToUserData());

Feel free to customize the structure to fit the needs of your game.

We have a comprehensive guide on how to leverage UserData to sync ODIN with a multiplayer game created using Mirror Networking .