Пример #1
0
        /// <summary>
        /// Deserialize a previously serialized game object.
        /// </summary>

        static void DeserializeComponents(this GameObject go, DataNode root)
        {
            DataNode scriptNode = root.GetChild("Components");

            if (scriptNode == null)
            {
                return;
            }

            for (int i = 0; i < scriptNode.children.size; ++i)
            {
                DataNode    node = scriptNode.children[i];
                System.Type type = UnityTools.GetType(node.name);

                if (type != null && type.IsSubclassOf(typeof(Component)))
                {
                    Component comp = go.GetComponent(type);
                    if (comp == null)
                    {
                        comp = go.AddComponent(type);
                    }
                    comp.Deserialize(node);
                }
            }
        }
Пример #2
0
    /// <summary>
    /// Create a packet that will send a custom object creation call.
    /// It is expected that the first byte that follows will identify which function will be parsing this packet later.
    /// </summary>

    static public void CreateEx(int rccID, bool persistent, GameObject go, params object[] objs)
    {
        if (go != null)
        {
            int index = IndexOf(go);

            if (isConnected)
            {
                if (index != -1)
                {
                    BinaryWriter writer = mInstance.mClient.BeginSend(Packet.RequestCreate);
                    writer.Write((ushort)index);
                    writer.Write(GetFlag(go, persistent));
                    writer.Write((byte)rccID);
                    writer.WriteArray(objs);
                    EndSend();
                    return;
                }
                else
                {
                    Debug.LogError("\"" + go.name + "\" has not been added to TNManager's list of objects, so it cannot be instantiated.\n" +
                                   "Consider placing it into the Resources folder and passing its name instead.", go);
                }
            }

            objs = BinaryExtensions.CombineArrays(go, objs);
            UnityTools.ExecuteAll(GetRCCs(), (byte)rccID, objs);
            UnityTools.Clear(objs);
        }
    }
Пример #3
0
    /// <summary>
    /// Create a packet that will send a custom object creation call.
    /// It is expected that the first byte that follows will identify which function will be parsing this packet later.
    /// </summary>

    static public void CreateEx(int rccID, bool persistent, string path, params object[] objs)
    {
        GameObject go = LoadGameObject(path);

        if (go != null)
        {
            if (isConnected)
            {
                if (mInstance != null && mInstance.mClient.isSwitchingScenes)
                {
                    Debug.LogWarning("Trying to create an object while switching scenes. Call will be ignored.");
                }

                BinaryWriter writer = mInstance.mClient.BeginSend(Packet.RequestCreate);
                byte         flag   = GetFlag(go, persistent);
                writer.Write((ushort)65535);
                writer.Write(flag);
                writer.Write(path);
                writer.Write((byte)rccID);
                writer.WriteArray(objs);
                EndSend();
                return;
            }

            objs = BinaryExtensions.CombineArrays(go, objs);
            UnityTools.ExecuteAll(GetRCCs(), (byte)rccID, objs);
            UnityTools.Clear(objs);
        }
        else
        {
            Debug.LogError("Unable to load " + path);
        }
    }
Пример #4
0
    /// <summary>
    /// Draw the list of known LAN servers.
    /// </summary>

    void DrawServerList(Rect rect)
    {
        GUI.color = new Color(1f, 1f, 1f, mAlpha * mAlpha * 0.5f);
        GUI.Box(UnityTools.PadRect(rect, 8f), "");
        GUI.color = new Color(1f, 1f, 1f, mAlpha * mAlpha);

        GUILayout.BeginArea(rect);
        {
            GUILayout.Label("LAN Server List", text);

            // List of discovered servers
            List <ServerList.Entry> list = TNLobbyClient.knownServers.list;

            // Server list example script automatically collects servers that have recently announced themselves
            for (int i = 0; i < list.size; ++i)
            {
                ServerList.Entry ent = list[i];

                // NOTE: I am using 'internalAddress' here because I know all servers are hosted on LAN.
                // If you are hosting outside of your LAN, you should probably use 'externalAddress' instead.
                if (GUILayout.Button(ent.internalAddress.ToString(), button))
                {
                    TNManager.Connect(ent.internalAddress, ent.internalAddress);
                    mMessage = "Connecting...";
                }
            }
        }
        GUILayout.EndArea();
        GUI.color = Color.white;
    }
Пример #5
0
        /// <summary>
        /// Serialize the entire mesh into the specified DataNode.
        /// </summary>

        static public void Serialize(this Mesh mesh, DataNode node)
        {
            if (!mFullSerialization)
            {
                return;
            }

            node.AddChild("name", mesh.name);
            string path = UnityTools.LocateResource(mesh);

            if (!string.IsNullOrEmpty(path))
            {
                node.AddChild("path", path);
                return;
            }

            Add(node, "vertices", mesh.vertices);
            Add(node, "normals", mesh.normals);
            Add(node, "uv1", mesh.uv);
            Add(node, "uv2", mesh.uv2);
            Add(node, "tangents", mesh.tangents);
            Add(node, "colors", mesh.colors32);
            Add(node, "weights", mesh.boneWeights);
            Add(node, "poses", mesh.bindposes);
            Add(node, "triangles", mesh.triangles);
        }
Пример #6
0
        /// <summary>
        /// Execute this function with the specified number of parameters.
        /// </summary>

        public object Execute(params object[] pars)
        {
            if (func == null)
            {
                return(null);
            }
            if (parameters == null)
            {
                parameters = func.GetParameters();
            }

            try
            {
                return((parameters.Length == 1 && parameters[0].ParameterType == typeof(object[])) ?
                       func.Invoke(obj, new object[] { pars }) :
                       func.Invoke(obj, pars));
            }
            catch (System.Exception ex)
            {
                if (ex.GetType() == typeof(System.NullReferenceException))
                {
                    return(null);
                }
                UnityTools.PrintException(ex, this, 0, func.Name, pars);
                return(null);
            }
        }
Пример #7
0
        /// <summary>
        /// Instantiate a new game object given its previously serialized DataNode.
        /// You can serialize game objects by using GameObject.Serialize(), but be aware that serializing only
        /// works fully in the Unity Editor. Prefabs can't be located automatically outside of the Unity Editor.
        /// </summary>

        static public GameObject Instantiate(this DataNode data)
        {
            GameObject child = null;

            byte[] assetBytes = data.GetChild <byte[]>("assetBundle");

            if (assetBytes != null)
            {
                GameObject prefab;

                if (!mCachedBundles.TryGetValue(assetBytes, out prefab))
                {
#if UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
                    AssetBundle qr = AssetBundle.CreateFromMemoryImmediate(assetBytes);
#else
                    AssetBundle qr = AssetBundle.LoadFromMemory(assetBytes);
#endif
                    if (qr != null)
                    {
                        prefab = qr.mainAsset as GameObject;
                    }
                    if (prefab == null)
                    {
                        prefab = new GameObject(data.name);
                    }
                    mCachedBundles[assetBytes] = prefab;
                }

                child      = GameObject.Instantiate(prefab) as GameObject;
                child.name = data.name;
            }
            else
            {
                string path = data.GetChild <string>("prefab");

                if (!string.IsNullOrEmpty(path))
                {
                    GameObject prefab = UnityTools.LoadPrefab(path);

                    if (prefab != null)
                    {
                        child      = GameObject.Instantiate(prefab) as GameObject;
                        child.name = data.name;
                        child.SetActive(true);
                    }
                    else
                    {
                        child = new GameObject(data.name);
                    }
                }
                else
                {
                    child = new GameObject(data.name);
                }

                child.Deserialize(data, true);
            }
            return(child);
        }
Пример #8
0
    /// <summary>
    /// Adjust the server list's alpha based on whether it should be shown or not.
    /// </summary>

    void Update()
    {
        if (Application.isPlaying)
        {
            float target = (TNLobbyClient.knownServers.list.size == 0) ? 0f : 1f;
            mAlpha = UnityTools.SpringLerp(mAlpha, target, 8f, Time.deltaTime);
        }
    }
Пример #9
0
    /// <summary>
    /// Invoke the function specified by the function name.
    /// </summary>

    public bool Execute(string funcName, params object[] parameters)
    {
        if (mParent != null)
        {
            return(mParent.Execute(funcName, parameters));
        }
        if (rebuildMethodList)
        {
            RebuildMethodList();
        }
        return(UnityTools.ExecuteAll(mRFCs, funcName, parameters));
    }
Пример #10
0
        /// <summary>
        /// Instantiate a new game object given its previously serialized DataNode.
        /// You can serialize game objects by using GameObject.Serialize(), but be aware that serializing only
        /// works fully in the Unity Editor. Prefabs can't be located automatically outside of the Unity Editor.
        /// </summary>

        static public GameObject Instantiate(this DataNode data)
        {
            GameObject child = null;

            byte[] assetBytes = data.GetChild <byte[]>("assetBundle");

            if (assetBytes != null)
            {
                AssetBundle ab = UnityTools.LoadAssetBundle(assetBytes);

                if (ab != null)
                {
                    var go = ab.mainAsset as GameObject;

                    if (go != null)
                    {
                        child      = GameObject.Instantiate(go) as GameObject;
                        child.name = data.name;
                    }
                }
            }
            else
            {
                string path = data.GetChild <string>("prefab");

                if (!string.IsNullOrEmpty(path))
                {
                    GameObject prefab = UnityTools.LoadPrefab(path);

                    if (prefab != null)
                    {
                        child      = GameObject.Instantiate(prefab) as GameObject;
                        child.name = data.name;
                        child.SetActive(true);
                    }
                    else
                    {
                        child = new GameObject(data.name);
                    }
                }
                else
                {
                    child = new GameObject(data.name);
                }

                child.Deserialize(data, true);
            }
            return(child);
        }
Пример #11
0
        /// <summary>
        /// Deserialize a previously serialized game object.
        /// </summary>

        static void DeserializeHierarchy(this GameObject go, DataNode root)
        {
            SerializationEntry ent = new SerializationEntry();

            ent.go   = go;
            ent.node = root;
            mSerList.Add(ent);

            Transform trans = go.transform;

            trans.localPosition    = root.GetChild <Vector3>("position", trans.localPosition);
            trans.localEulerAngles = root.GetChild <Vector3>("rotation", trans.localEulerAngles);
            trans.localScale       = root.GetChild <Vector3>("scale", trans.localScale);
            go.layer = root.GetChild <int>("layer", go.layer);

            DataNode childNode = root.GetChild("Children");

            if (childNode != null && childNode.children.size > 0)
            {
                for (int i = 0; i < childNode.children.size; ++i)
                {
                    DataNode   node   = childNode.children[i];
                    GameObject child  = null;
                    GameObject prefab = UnityTools.Load <GameObject>(node.GetChild <string>("prefab"));
                    if (prefab != null)
                    {
                        child = GameObject.Instantiate(prefab) as GameObject;
                    }
                    if (child == null)
                    {
                        child = new GameObject();
                    }
                    child.name = node.name;

                    Transform t = child.transform;
                    t.parent        = trans;
                    t.localPosition = Vector3.zero;
                    t.localRotation = Quaternion.identity;
                    t.localScale    = Vector3.one;

                    child.DeserializeHierarchy(node);
                }
            }
        }
Пример #12
0
    /// <summary>
    /// On success -- join a channel.
    /// </summary>

    void OnNetworkConnect(bool result, string message)
    {
        if (result)
        {
            // Make it possible to use UDP using a random port
            if (allowUDP)
            {
                TNManager.StartUDP(Random.Range(10000, 50000));
            }
            TNManager.JoinChannel(channelID, firstLevel, persistent, 10000, null);
        }
        else if (!string.IsNullOrEmpty(failureFunctionName))
        {
            UnityTools.Broadcast(failureFunctionName, message);
        }
        else
        {
            Debug.LogError(message);
        }
    }
Пример #13
0
    /// <summary>
    /// Joined a channel (or failed to).
    /// </summary>

    void OnNetworkJoinChannel(bool result, string message)
    {
        if (result)
        {
            if (!string.IsNullOrEmpty(successFunctionName))
            {
                UnityTools.Broadcast(successFunctionName);
            }
        }
        else
        {
            if (!string.IsNullOrEmpty(failureFunctionName))
            {
                UnityTools.Broadcast(failureFunctionName, message);
            }
            else
            {
                Debug.LogError(message);
            }

            TNManager.Disconnect();
        }
    }
Пример #14
0
    /// <summary>
    /// Create a new game object.
    /// </summary>

    static GameObject CreateGameObject(GameObject prefab, BinaryReader reader)
    {
        if (prefab != null)
        {
            // The first byte is always the type that identifies what kind of data will follow
            byte type = reader.ReadByte();

            if (type == 0)
            {
                // Just a plain game object
                return(Instantiate(prefab) as GameObject);
            }
            else
            {
                // Custom creation function
                object[] objs = reader.ReadArray(prefab);
                object   retVal;

                if (!UnityTools.ExecuteFirst(GetRCCs(), type, out retVal, objs))
                {
                    Debug.LogError("[TNet] Failed to call RCC #" + type + ".\nDid you forget to register it in Awake() via TNManager.AddRCCs?");
                    UnityTools.Clear(objs);
                    return(null);
                }

                UnityTools.Clear(objs);

                if (retVal == null)
                {
                    Debug.LogError("[TNet] Instantiating \"" + prefab.name + "\" via RCC #" + type + " returned null.\nDid you forget to return the game object from your RCC?");
                }
                return(retVal as GameObject);
            }
        }
        return(null);
    }
Пример #15
0
    /// <summary>
    /// Notification of a player being renamed.
    /// </summary>

    void OnRenamePlayer(Player p, string previous)
    {
        mPlayer.name = p.name;
        UnityTools.Broadcast("OnNetworkPlayerRenamed", p, previous);
    }
Пример #16
0
    /// <summary>
    /// Notification of another player leaving the channel.
    /// </summary>

    void OnPlayerLeft(Player p)
    {
        UnityTools.Broadcast("OnNetworkPlayerLeave", p);
    }
Пример #17
0
    /// <summary>
    /// Notification sent when leaving a channel.
    /// Also sent just before a disconnect (if inside a channel when it happens).
    /// </summary>

    void OnLeftChannel()
    {
        mJoiningChannel = false;
        UnityTools.Broadcast("OnNetworkLeaveChannel");
    }
Пример #18
0
	/// <summary>
	/// Invoke the function specified by the ID.
	/// </summary>

	public bool Execute (byte funcID, params object[] parameters)
	{
		if (mParent != null) return mParent.Execute(funcID, parameters);
		if (rebuildMethodList) RebuildMethodList();
		return UnityTools.ExecuteAll(mRFCs, funcID, parameters);
	}
Пример #19
0
    /// <summary>
    /// Notification that happens when the client gets disconnected from the server.
    /// </summary>

    void OnDisconnect()
    {
        UnityTools.Broadcast("OnNetworkDisconnect");
    }
Пример #20
0
    /// <summary>
    /// Error notification.
    /// </summary>

    void OnError(string err)
    {
        UnityTools.Broadcast("OnNetworkError", err);
    }
Пример #21
0
        /// <summary>
        /// Load a game object prefab at the specified path. This is equivalent to Resources.Load, but it will
        /// also consider DataNode-exported binary assets as well, automatically loading them as if they were
        /// regular prefabs.
        /// </summary>

        static public GameObject LoadPrefab(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return(null);
            }
            if (!Application.isPlaying)
            {
                return(Resources.Load(path, typeof(GameObject)) as GameObject);
            }

            GameObject prefab = null;

            if (mPrefabRoot == null)
            {
                GameObject go = new GameObject("Prefabs");
                Object.DontDestroyOnLoad(go);
                mPrefabRoot = go.transform;
                mPrefabs.Clear();
            }

            // Try to get it from cache
            if (mPrefabs.TryGetValue(path, out prefab))
            {
                return(prefab);
            }

            if (prefab == null)
            {
                // Load it from resources as a Game Object
                prefab = Resources.Load(path, typeof(GameObject)) as GameObject;

                if (prefab == null)
                {
                    // Load it from resources as a binary asset
                    byte[] bytes = UnityTools.LoadBinary(path);

                    if (bytes != null)
                    {
                        // Parse the DataNode hierarchy
                        DataNode data = DataNode.Read(bytes);

                        if (data != null)
                        {
                            // Instantiate and immediately disable the object
                            prefab = data.Instantiate();

                            if (prefab != null)
                            {
                                mPrefabs.Add(path, prefab);
                                Object.DontDestroyOnLoad(prefab);
                                prefab.transform.parent = mPrefabRoot;
                                prefab.SetActive(false);
                                return(prefab);
                            }
                        }
                    }
                }
            }

            if (prefab == null)
            {
#if UNITY_EDITOR
                Debug.LogError("[TNet] Attempting to create a game object that can't be found in the Resources folder: [" + path + "]");
#endif
                prefab = GetDummyObject();
            }

            mPrefabs.Add(path, prefab);
            return(prefab);
        }
Пример #22
0
        /// <summary>
        /// Convert a serialized string reference to an actual object reference.
        /// </summary>

        static public UnityEngine.Object StringToReference(this GameObject go, string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return(null);
            }
            string[] split = path.Split(new char[] { '|' }, 3);

            if (split.Length == 3)
            {
                System.Type myType = UnityTools.FindType(split[1]);

                if (myType != null)
                {
                    if (myType == typeof(Shader))
                    {
                        return(Shader.Find(split[2]));
                    }
                    else if (split[0] == "asset")
                    {
                        return(Resources.Load(split[2], myType));
                    }
                    else if (split[0] == "ref")
                    {
                        Transform t = go.transform;

                        string[] splitPath = split[2].Split('/');

                        for (int i = 0; i < splitPath.Length; ++i)
                        {
                            string s = splitPath[i];

                            if (s == "..")
                            {
                                t = t.parent;
                                if (t == null)
                                {
                                    break;
                                }
                            }
                            else if (!string.IsNullOrEmpty(s))
                            {
                                t = t.FindChild(s);
                                if (t == null)
                                {
                                    break;
                                }
                            }
                        }

                        if (t != null)
                        {
                            if (myType == typeof(GameObject))
                            {
                                return(t.gameObject);
                            }
                            return(t.GetComponent(myType));
                        }
                        else
                        {
                            Debug.LogWarning("Hierarchy path not found: " + split[2], go);
                        }
                    }
                }
            }
            return(null);
        }
Пример #23
0
        /// <summary>
        /// Execute this function with the specified number of parameters.
        /// </summary>

        public object Execute(params object[] pars)
        {
            if (mi == null)
            {
                return(null);
            }

            var parameters = this.parameters;

            if (pars == null && mParamCount != 0)
            {
                pars = new object[parameters.Length];
            }
            if (mParamCount == 1 && parameters[0].ParameterType == typeof(object[]))
            {
                pars = new object[] { pars }
            }
            ;

            try
            {
                if (mAutoCast)
                {
                    for (int i = 0; i < mParamCount; ++i)
                    {
                        var passed = pars[i].GetType();
                        if (mTypes[i] != passed)
                        {
                            pars[i] = Serialization.CastValue(pars[i], mTypes[i]);
                        }
                    }
                }
                return(mi.Invoke(obj, pars));
            }
            catch (Exception ex)
            {
                if (ex.GetType() == typeof(NullReferenceException))
                {
                    return(null);
                }

                var tryAgain = false;

                if (mParamCount == pars.Length)
                {
                    if (mTypes == null)
                    {
                        mTypes = new Type[mParamCount];
                        for (int i = 0; i < mParamCount; ++i)
                        {
                            mTypes[i] = parameters[i].ParameterType;
                        }
                    }

                    for (int i = 0; i < mParamCount; ++i)
                    {
                        var passed = (pars[i] != null) ? pars[i].GetType() : mTypes[i];

                        if (mTypes[i] != passed)
                        {
                            pars[i] = Serialization.CastValue(pars[i], mTypes[i]);
                            if (pars[i] != null)
                            {
                                tryAgain = true;
                            }
                        }
                    }
                }

                if (tryAgain)
                {
                    try
                    {
                        if (parameters.Length == 1 && parameters[0].ParameterType == typeof(object[]))
                        {
                            pars = new object[] { pars }
                        }
                        ;
                        var retVal = mi.Invoke(obj, pars);
                        mAutoCast = true;
                        return(retVal);
                    }
                    catch (Exception ex2) { ex = ex2; }
                }

                UnityTools.PrintException(ex, this, 0, mi.Name, pars);
                return(null);
            }
        }
    }
Пример #24
0
 [RCC(2)] static GameObject OnCreate2(GameObject go, Vector3 pos, Quaternion rot, Vector3 velocity, Vector3 angularVelocity)
 {
     return(UnityTools.Instantiate(go, pos, rot, velocity, angularVelocity));
 }
Пример #25
0
    /// <summary>
    /// Notification sent when attempting to join a channel, indicating a success or failure.
    /// </summary>

    void OnJoinChannel(bool success, string message)
    {
        UnityTools.Broadcast("OnNetworkJoinChannel", success, message);
    }
Пример #26
0
    /// <summary>
    /// Notification sent when leaving a channel.
    /// Also sent just before a disconnect (if inside a channel when it happens).
    /// </summary>

    void OnLeftChannel()
    {
        UnityTools.Broadcast("OnNetworkLeaveChannel");
    }
Пример #27
0
    /// <summary>
    /// This menu is shown if the client has not yet connected to the server.
    /// </summary>

    void DrawConnectMenu()
    {
        Rect rect = new Rect(Screen.width * 0.5f - 200f * 0.5f - mAlpha * 120f,
                             Screen.height * 0.5f - 100f, 200f, 220f);

        // Show a half-transparent box around the upcoming UI
        GUI.color = new Color(1f, 1f, 1f, 0.5f);
        GUI.Box(UnityTools.PadRect(rect, 8f), "");
        GUI.color = Color.white;

        GUILayout.BeginArea(rect);
        {
            GUILayout.Label("Server Address", text);
            mAddress = GUILayout.TextField(mAddress, input, GUILayout.Width(200f));

            if (GUILayout.Button("Connect", button))
            {
                // We want to connect to the specified destination when the button is clicked on.
                // "OnConnect" function will be called sometime later with the result.
                TNManager.Connect(mAddress);
                mMessage = "Connecting...";
            }

            if (TNServerInstance.isActive)
            {
                GUI.backgroundColor = Color.red;

                if (GUILayout.Button("Stop the Server", button))
                {
                    // Stop the server, saving all the data
                    TNServerInstance.Stop();
                    mMessage = "Server stopped";
                }
            }
            else
            {
                GUI.backgroundColor = Color.green;

                if (GUILayout.Button("Start a LAN Server", button))
                {
#if UNITY_WEBPLAYER
                    mMessage = "Can't host from the Web Player due to Unity's security restrictions";
#else
                    // Start a local server, loading the saved data if possible
                    // The UDP port of the server doesn't matter much as it's optional,
                    // and the clients get notified of it via Packet.ResponseSetUDP.
                    int           udpPort = Random.Range(10000, 40000);
                    TNLobbyClient lobby   = GetComponent <TNLobbyClient>();

                    if (lobby == null)
                    {
                        if (TNServerInstance.Start(serverTcpPort, udpPort, "server.dat"))
                        {
                            TNManager.Connect();
                        }
                    }
                    else
                    {
                        TNServerInstance.Type type = (lobby is TNUdpLobbyClient) ?
                                                     TNServerInstance.Type.Udp : TNServerInstance.Type.Tcp;

                        if (TNServerInstance.Start(serverTcpPort, udpPort, lobby.remotePort, "server.dat", type))
                        {
                            TNManager.Connect();
                        }
                    }
                    mMessage = "Server started";
#endif
                }

                // Start a local server that doesn't use sockets. It's ideal for testing and for single player gameplay.
                if (GUILayout.Button("Start a Virtual Server", button))
                {
                    mMessage = "Server started";
                    TNServerInstance.Start("server.dat");
                    TNManager.Connect();
                }
            }
            GUI.backgroundColor = Color.white;

            if (!string.IsNullOrEmpty(mMessage))
            {
                GUILayout.Label(mMessage, text);
            }
        }
        GUILayout.EndArea();

        if (mAlpha > 0.01f)
        {
            rect.x = rect.x + (Screen.width - rect.xMin - rect.xMax) * mAlpha;
            DrawServerList(rect);
        }
    }
Пример #28
0
    /// <summary>
    /// Notification of a new player joining the channel.
    /// </summary>

    void OnPlayerJoined(Player p)
    {
        UnityTools.Broadcast("OnNetworkPlayerJoin", p);
    }
Пример #29
0
    /// <summary>
    /// Connection result notification.
    /// </summary>

    void OnConnect(bool success, string message)
    {
        UnityTools.Broadcast("OnNetworkConnect", success, message);
    }
Пример #30
0
    /// <summary>
    /// Notification that happens when the client gets disconnected from the server.
    /// </summary>

    void OnDisconnect()
    {
        mJoiningChannel = false;
        UnityTools.Broadcast("OnNetworkDisconnect");
    }