/// <summary>
        /// Spawns the object.
        /// </summary>
        /// <param name="obj">The object to spawn.</param>
        public void SpawnObject(GameObject obj)
        {
            if (!isRunning)
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("SpawnObject for " + obj + ", NetworkServer is not active. Cannot spawn objects without an active server.");
                }
                return;
            }

            TinyNetIdentity objTinyNetIdentity;

            if (!GetTinyNetIdentity(obj, out objTinyNetIdentity))
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("SpawnObject " + obj + " has no TinyNetIdentity. Please add a TinyNetIdentity to " + obj);
                }
                return;
            }

            objTinyNetIdentity.OnNetworkCreate();

            objTinyNetIdentity.OnStartServer(false);

            AddTinyNetIdentityToList(objTinyNetIdentity);

            if (TinyNetLogLevel.logDebug)
            {
                TinyLogger.Log("SpawnObject instance ID " + objTinyNetIdentity.TinyInstanceID + " asset GUID " + objTinyNetIdentity.assetGUID);
            }

            // Using ShowObjectToConnection prevents the server from sending spawn messages of objects that are already spawned.
            for (int i = 0; i < tinyNetConns.Count; i++)
            {
                tinyNetConns[i].ShowObjectToConnection(objTinyNetIdentity);
            }
        }
        //============ Players Methods ======================//

        /// <summary>
        /// Attempts to add a player controller to the connection.
        /// </summary>
        /// <param name="conn">The connection.</param>
        /// <param name="playerControllerId">The player controller identifier.</param>
        protected virtual void AddPlayerControllerToConnection(TinyNetConnection conn, int playerControllerId)
        {
            if (playerControllerId < 0)
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("AddPlayerControllerToConnection() called with playerControllerId < 0");
                }
                return;
            }

            if (playerControllerId < conn.playerControllers.Count && conn.playerControllers[playerControllerId].IsValid)
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("There is already a player with that playerControllerId for this connection");
                }
                return;
            }

            CreatePlayerAndAdd(conn, playerControllerId);
        }
        /// <summary>
        /// Called when an object is destroyed.
        /// </summary>
        /// <param name="netMsg">A wrapper for a <see cref="TinyNetObjectDestroyMessage"/>.</param>
        void OnObjectDestroy(TinyNetMessageReader netMsg)
        {
            netMsg.ReadMessage(s_TinyNetObjectDestroyMessage);

            if (TinyNetLogLevel.logDebug)
            {
                TinyLogger.Log("TinyNetClient::OnObjDestroy networkID:" + s_TinyNetObjectDestroyMessage.networkID);
            }

            TinyNetIdentity localObject = _localIdentityObjects[s_TinyNetObjectDestroyMessage.networkID];

            if (localObject != null)
            {
                localObject.OnNetworkDestroy();

                if (!TinyNetGameManager.instance.InvokeUnSpawnHandler(localObject.assetGUID, localObject.gameObject))
                {
                    // default handling
                    if (localObject.sceneID == 0)
                    {
                        Object.Destroy(localObject.gameObject);
                    }
                    else
                    {
                        // scene object.. disable it in scene instead of destroying
                        localObject.gameObject.SetActive(false);
                        _sceneIdentityObjectsToSpawn[localObject.sceneID] = localObject;
                    }
                }
            }
            else
            {
                if (TinyNetLogLevel.logDebug)
                {
                    TinyLogger.LogWarning("Did not find target for destroy message for " + s_TinyNetObjectDestroyMessage.networkID);
                }
            }
        }
Beispiel #4
0
        /// <summary>
        /// Starts the client.
        /// </summary>
        /// <returns></returns>
        public virtual bool StartClient()
        {
            if (_netManager != null)
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("[" + TYPE + "] StartClient() called multiple times.");
                }
                return(false);
            }

            _netManager = new NetManager(this);
            _netManager.Start();

            ConfigureNetManager(true);

            if (TinyNetLogLevel.logDev)
            {
                TinyLogger.Log("[" + TYPE + "] Started client");
            }

            return(true);
        }
        /// <summary>
        /// By default it will deserialize the <see cref="TinyNetSyncVar"/> properties.
        /// </summary>
        /// <param name="netMsg">A wrapper for a <see cref="TinyNetObjectStateUpdate"/> message.</param>
        void OnStateUpdateMessage(TinyNetMessageReader netMsg)
        {
            int networkID = netMsg.reader.GetInt();

            if (TinyNetLogLevel.logDev)
            {
                TinyLogger.Log("TinyNetClient::OnUpdateVarsMessage " + networkID + " channel:" + netMsg.channelId);
            }

            ITinyNetObject localObject = _localNetObjects[networkID];

            if (localObject != null)
            {
                localObject.TinyDeserialize(netMsg.reader, false);
            }
            else
            {
                if (TinyNetLogLevel.logWarn)
                {
                    TinyLogger.LogWarning("Did not find target for sync message for " + networkID);
                }
            }
        }
        /// <summary>
        /// Starts the server.
        /// </summary>
        /// <param name="port">The port.</param>
        /// <param name="maxNumberOfPlayers">The maximum number of players.</param>
        /// <returns></returns>
        public virtual bool StartServer(int port, int maxNumberOfPlayers)
        {
            if (_netManager != null)
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("[" + TYPE + "] StartServer() called multiple times.");
                }
                return(false);
            }

            _netManager = new NetManager(this);
            _netManager.Start(port);

            ConfigureNetManager(true);

            if (TinyNetLogLevel.logDev)
            {
                TinyLogger.Log("[" + TYPE + "] Started server at port: " + port + " with maxNumberOfPlayers: " + maxNumberOfPlayers);
            }

            return(true);
        }
        /// <summary>
        /// Called when a <see cref="TinyNetRequestAddPlayerMessage"/> is received.
        /// </summary>
        /// <param name="netMsg">The net message.</param>
        void OnRequestAddPlayerMessage(TinyNetMessageReader netMsg)
        {
            netMsg.ReadMessage(s_TinyNetRequestAddPlayerMessage);

            if (s_TinyNetRequestAddPlayerMessage.amountOfPlayers <= 0)
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("OnRequestAddPlayerMessage() called with amountOfPlayers <= 0");
                }
                return;
            }

            // Check here if you should create another player controller for that connection.

            int playerId = TinyNetGameManager.instance.NextPlayerID;            //netMsg.tinyNetConn.playerControllers.Count;

            AddPlayerControllerToConnection(netMsg.tinyNetConn, playerId);

            // Tell the origin client to add them too!
            s_TinyNetAddPlayerMessage.playerControllerId = (short)playerId;
            SendMessageByChannelToTargetConnection(s_TinyNetAddPlayerMessage, DeliveryMethod.ReliableOrdered, netMsg.tinyNetConn);
        }
Beispiel #8
0
        /// <summary>
        /// Called when a scene object is spawned.
        /// </summary>
        /// <param name="netMsg">A wrapper for a <see cref="TinyNetObjectSpawnSceneMessage"/>.</param>
        void OnObjectSpawnScene(TinyNetMessageReader netMsg)
        {
            netMsg.ReadMessage(s_TinyNetObjectSpawnSceneMessage);

            if (TinyNetLogLevel.logDebug)
            {
                TinyLogger.Log("Client spawn scene handler instantiating [networkID: " + s_TinyNetObjectSpawnSceneMessage.networkID + " sceneId: " + s_TinyNetObjectSpawnSceneMessage.sceneId + " pos: " + s_TinyNetObjectSpawnSceneMessage.position);
            }

            TinyNetIdentity localTinyNetIdentity = GetTinyNetIdentityByNetworkID(s_TinyNetObjectSpawnSceneMessage.networkID);

            if (localTinyNetIdentity != null)
            {
                // this object already exists (was in the scene)
                ApplyInitialState(localTinyNetIdentity, s_TinyNetObjectSpawnSceneMessage.position, s_TinyNetObjectSpawnSceneMessage.initialState, s_TinyNetObjectSpawnSceneMessage.networkID, localTinyNetIdentity.gameObject, s_TinyNetObjectSpawnSceneMessage.frameTick);
                return;
            }

            TinyNetIdentity spawnedId = SpawnSceneObject(s_TinyNetObjectSpawnSceneMessage.sceneId);

            if (spawnedId == null)
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("Spawn scene object not found for " + s_TinyNetObjectSpawnSceneMessage.sceneId);
                }
                return;
            }

            if (TinyNetLogLevel.logDebug)
            {
                TinyLogger.Log("Client spawn for [networkID :" + s_TinyNetObjectSpawnSceneMessage.networkID + "] [sceneId: " + s_TinyNetObjectSpawnSceneMessage.sceneId + "] obj: " + spawnedId.gameObject.name);
            }

            ApplyInitialState(spawnedId, s_TinyNetObjectSpawnSceneMessage.position, s_TinyNetObjectSpawnSceneMessage.initialState, s_TinyNetObjectSpawnSceneMessage.networkID, spawnedId.gameObject, s_TinyNetObjectSpawnSceneMessage.frameTick);
        }
        /// <summary>
        /// Removes the player controller from this connection.
        /// </summary>
        /// <param name="playerControllerId">The player controller identifier.</param>
        public void RemovePlayerController(short playerControllerId)
        {
            /*int count = _playerControllers.Count;
             *
             * while (count >= 0) {
             *      if (playerControllerId == count && playerControllerId == _playerControllers[count].playerControllerId) {
             *              _playerControllers[count] = new TinyNetPlayerController();
             *              return;
             *      }
             *      count -= 1;
             * }*/
            TinyNetPlayerController tPC;

            if (GetPlayerController(playerControllerId, out tPC))
            {
                _playerControllers.Remove(tPC);
                return;
            }

            if (TinyNetLogLevel.logError)
            {
                TinyLogger.LogError("RemovePlayerController for playerControllerId " + playerControllerId + " not found");
            }
        }
Beispiel #10
0
        /// <summary>
        /// Called when an object is spawned.
        /// </summary>
        /// <param name="netMsg">A wrapper for a <see cref="TinyNetObjectSpawnMessage"/>.</param>
        void OnObjectSpawn(TinyNetMessageReader netMsg)
        {
            netMsg.ReadMessage(s_TinyNetObjectSpawnMessage);

            if (s_TinyNetObjectSpawnMessage.assetIndex < 0 || s_TinyNetObjectSpawnMessage.assetIndex > int.MaxValue || s_TinyNetObjectSpawnMessage.assetIndex > TinyNetGameManager.instance.GetAmountOfRegisteredAssets())
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("OnObjSpawn networkID: " + s_TinyNetObjectSpawnMessage.networkID + " has invalid asset Id");
                }
                return;
            }
            if (TinyNetLogLevel.logDev)
            {
                TinyLogger.Log("Client spawn handler instantiating [networkID:" + s_TinyNetObjectSpawnMessage.networkID + " asset ID:" + s_TinyNetObjectSpawnMessage.assetIndex + " pos:" + s_TinyNetObjectSpawnMessage.position + "]");
            }

            TinyNetIdentity localTinyNetIdentity = GetTinyNetIdentityByNetworkID(s_TinyNetObjectSpawnMessage.networkID);

            // Check if this object was already registered in the scene:
            if (localTinyNetIdentity != null)
            {
                // this object already exists (was in the scene), just apply the update to existing object.
                ApplyInitialState(localTinyNetIdentity, s_TinyNetObjectSpawnMessage.position, s_TinyNetObjectSpawnMessage.initialState, s_TinyNetObjectSpawnMessage.networkID, null, s_TinyNetObjectSpawnMessage.frameTick);
                return;
            }

            GameObject    prefab;
            SpawnDelegate handler;

            prefab = TinyNetGameManager.instance.GetPrefabFromAssetId(s_TinyNetObjectSpawnMessage.assetIndex);
            // Check if the prefab is registered in the list of spawnable prefabs.
            if (prefab != null)
            {
                var obj = (GameObject)Object.Instantiate(prefab, s_TinyNetObjectSpawnMessage.position, Quaternion.identity);

                localTinyNetIdentity = obj.GetComponent <TinyNetIdentity>();

                if (localTinyNetIdentity == null)
                {
                    if (TinyNetLogLevel.logError)
                    {
                        TinyLogger.LogError("Client object spawned for " + s_TinyNetObjectSpawnMessage.assetIndex + " does not have a TinyNetidentity");
                    }
                    return;
                }

                ApplyInitialState(localTinyNetIdentity, s_TinyNetObjectSpawnMessage.position, s_TinyNetObjectSpawnMessage.initialState, s_TinyNetObjectSpawnMessage.networkID, obj, s_TinyNetObjectSpawnMessage.frameTick);
                // If not, check if the prefab have a spawn handler registered.
            }
            else if (TinyNetGameManager.instance.GetSpawnHandler(s_TinyNetObjectSpawnMessage.assetIndex, out handler))
            {
                GameObject obj = handler(s_TinyNetObjectSpawnMessage.position, s_TinyNetObjectSpawnMessage.assetIndex);
                if (obj == null)
                {
                    if (TinyNetLogLevel.logWarn)
                    {
                        TinyLogger.LogWarning("Client spawn handler for " + s_TinyNetObjectSpawnMessage.assetIndex + " returned null");
                    }
                    return;
                }

                localTinyNetIdentity = obj.GetComponent <TinyNetIdentity>();
                if (localTinyNetIdentity == null)
                {
                    if (TinyNetLogLevel.logError)
                    {
                        TinyLogger.LogError("Client object spawned for " + s_TinyNetObjectSpawnMessage.assetIndex + " does not have a network identity");
                    }
                    return;
                }

                localTinyNetIdentity.SetDynamicAssetGUID(TinyNetGameManager.instance.GetAssetGUIDFromAssetId(s_TinyNetObjectSpawnMessage.assetIndex));
                ApplyInitialState(localTinyNetIdentity, s_TinyNetObjectSpawnMessage.position, s_TinyNetObjectSpawnMessage.initialState, s_TinyNetObjectSpawnMessage.networkID, obj, s_TinyNetObjectSpawnMessage.frameTick);
                // If also not, we literally cannot spawn this object and you should feel bad.
            }
            else
            {
                if (TinyNetLogLevel.logError)
                {
                    TinyLogger.LogError("Failed to spawn server object, assetId=" + s_TinyNetObjectSpawnMessage.assetIndex + " networkID=" + s_TinyNetObjectSpawnMessage.networkID);
                }
            }
        }
Beispiel #11
0
        /// <summary>
        /// Called when an object is spawned on the server.
        /// <para> Called on the server when Spawn is called for this object. (Order: 1) </para>
        /// </summary>
        /// <param name="allowAlreadySetId">If the object already have a NetworkId, it was probably recycled.</param>
        public void OnStartServer(bool allowAlreadySetId)
        {
            if (_localPlayerAuthority)
            {
                // local player on server has NO authority
                HasAuthority = false;
            }
            else
            {
                // enemy on server has authority
                HasAuthority = true;
            }

            // If the instance/net ID is invalid here then this is an object instantiated from a prefab and the server should assign a valid ID
            if (TinyInstanceID == null)
            {
                TinyInstanceID = TinyNetGameManager.instance.NextNetworkID;
            }
            else
            {
                if (allowAlreadySetId)
                {
                    //allowed
                }
                else
                {
                    if (TinyNetLogLevel.logError)
                    {
                        TinyLogger.LogError("Object has already set netId " + TinyInstanceID + " for " + gameObject);
                    }
                    return;
                }
            }

            // If anything goes wrong, blame this person: https://forum.unity.com/threads/getcomponentsinchildren.4582/#post-33983
            for (int i = 0; i < _tinyNetComponents.Length; i++)
            {
                _tinyNetComponents[i].ReceiveNetworkID(new TinyNetworkID(TinyInstanceID.NetworkID, (byte)(i + 1)));
            }

            {             // Calling OnStartServer on those who registered
                LinkedList <System.Action> handlers;
                if (!_registeredEventHandlers.TryGetValue(TinyNetComponentEvents.OnStartServer, out handlers))
                {
                    return;
                }

                for (LinkedListNode <System.Action> currentNode = handlers.First; currentNode != null; currentNode = currentNode.Next)
                {
                    currentNode.Value();
                }
            }

            if (TinyNetLogLevel.logDev)
            {
                TinyLogger.Log("OnStartServer " + gameObject + " netId:" + TinyInstanceID);
            }

            if (HasAuthority)
            {
                OnStartAuthority();
            }
        }
Beispiel #12
0
        /// <summary>
        /// Deserializes all <see cref="ITinyNetComponent"/> data.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <param name="bInitialState">if set to <c>true</c> [b initial state].</param>
        public virtual void TinyDeserialize(TinyNetStateReader reader, bool firstStateUpdate)
        {
            if (firstStateUpdate && _tinyNetComponents == null)
            {
                if (TinyNetLogLevel.logWarn)
                {
                    TinyLogger.LogWarning("TinyNetIdentity::TinyDeserialize called with firstStateUpdate true, but _tinyNetComponents is null.");
                }
                CacheTinyNetObjects();
            }

            if (firstStateUpdate)
            {
                for (int i = 0; i < _tinyNetComponents.Length; i++)
                {
                    _tinyNetComponents[i].ReceiveNetworkID(new TinyNetworkID(TinyInstanceID.NetworkID, (byte)(i + 1)));

                    _recycleReader.Clear();
                    int rSize = reader.GetInt();
                    _recycleReader.SetSource(reader.RawData, reader.Position, rSize);

                    _recycleReader.SetFrameTick(reader.FrameTick);
                    _tinyNetComponents[i].TinyDeserialize(_recycleReader, firstStateUpdate);
                    // We jump the reader position to the amount of data we read.
                    reader.SkipBytes(rSize);
                }

                return;
            }

            switch (_dirtyFlag.Length)
            {
            case 8:
                TinyBitArrayUtil.U64ToBitArray(reader.GetByte(), _dirtyFlag);
                break;

            case 16:
                TinyBitArrayUtil.U64ToBitArray(reader.GetUShort(), _dirtyFlag);
                break;

            case 32:
                TinyBitArrayUtil.U64ToBitArray(reader.GetUInt(), _dirtyFlag);
                break;

            case 64:
                TinyBitArrayUtil.U64ToBitArray(reader.GetULong(), _dirtyFlag);
                break;
            }

            for (int i = 0; i < _tinyNetComponents.Length; i++)
            {
                if (_dirtyFlag[i] == true)
                {
                    _recycleReader.Clear();
                    int rSize = reader.GetInt();
                    _recycleReader.SetSource(reader.RawData, reader.Position, rSize);

                    _recycleReader.SetFrameTick(reader.FrameTick);
                    //Debug.Log("[Deserialize] Size: " + rSize + ", DirtyFlag: " + TinyBitArrayUtil.Display(_recycleReader.PeekByte()));
                    _tinyNetComponents[i].TinyDeserialize(_recycleReader, firstStateUpdate);
                    // We jump the reader position to the amount of data we read.
                    reader.SkipBytes(rSize);
                }
            }
        }
		/// <summary>
		/// Does the reflection process.
		/// </summary>
		public static void GetAllSyncVarProps() {
			List<Type> types = GetAllClassesAndChildsOf<TinyNetBehaviour>();

			foreach (Type type in types) {
				PropertyInfo[] props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
					.Where(prop => Attribute.IsDefined(prop, typeof(TinyNetSyncVar)))
					.ToArray();
					//.OrderBy(info => info.Name).ToArray();
				Array.Sort(props, delegate(PropertyInfo x, PropertyInfo y) { return String.Compare(x.Name, y.Name, StringComparison.InvariantCulture); });

				if (props.Length < 32) {

					TinyNetStateSyncer.InitializePropertyInfoListOfType(props.Length, type);

					for (int i = 0; i < props.Length; i++) {
						if (TinyNetSyncVar.allowedTypes.Contains(props[i].PropertyType)) {
							if (TinyNetLogLevel.logDev) { TinyLogger.Log(props[i].Name); }

							//MethodInfo getMethod = props[i].GetGetMethod(true);
							//MethodInfo setMethod = props[i].GetSetMethod(true);

							TinyNetStateSyncer.AddPropertyToType(props[i], type);
						} else {
							if (TinyNetLogLevel.logError) { TinyLogger.LogError("TinyNetSyncVar used in incompatible property type: " + props[i].Name); }
						}
					}
				} else {
					if (TinyNetLogLevel.logError) { TinyLogger.LogError("ERROR: " + type + " have more than 32 syncvar"); }
				}

				// Time for the RPC methods
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
					.Where(method => Attribute.IsDefined(method, typeof(TinyNetRPC)))
					.ToArray();
					//.OrderBy(info => info.Name).ToArray();
				Array.Sort(methods, delegate (MethodInfo x, MethodInfo y) { return String.Compare(x.Name, y.Name, StringComparison.InvariantCulture); });

				TinyNetStateSyncer.InitializeRPCMethodsOfType(methods.Length, type);

				ParameterInfo[] pars;
				bool bValid = true;
				TinyNetRPC rpcAttribute;

				for (int i = 0; i < methods.Length; i++) {
					pars = methods[i].GetParameters();
					rpcAttribute = (TinyNetRPC)methods[i].GetCustomAttributes(typeof(TinyNetRPC), true)[0];

					bValid = true;
					for (int x = 0; x < pars.Length; x++) {
						if (!TinyNetSyncVar.allowedTypes.Contains(pars[x].ParameterType)) {
							if (TinyNetLogLevel.logError) { TinyLogger.LogError("TinyNetRPC used with incompatible parameter: " + pars[x].Name); }
							bValid = false;
						}
					}

					if (bValid) {
						if (TinyNetLogLevel.logDev) { TinyLogger.Log(methods[i].Name); }

						TinyNetStateSyncer.AddRPCMethodNameToType(methods[i].Name, rpcAttribute.Targets, rpcAttribute.Callers, type);
					}
				}
			}
		}
        //============ Objects Methods ======================//

        /// <summary>
        /// Spawns all TinyNetIdentity objects in the scene.
        /// </summary>
        /// <returns>
        /// This actually always return true?
        /// </returns>
        public bool SpawnAllObjects()
        {
            if (isRunning)
            {
                TinyNetIdentity[] tnis = Resources.FindObjectsOfTypeAll <TinyNetIdentity>();

                foreach (var tni in tnis)
                {
                    if (tni.gameObject.hideFlags == HideFlags.NotEditable || tni.gameObject.hideFlags == HideFlags.HideAndDontSave)
                    {
                        continue;
                    }

                    if (tni.sceneID == 0)
                    {
                        continue;
                    }

                    if (TinyNetLogLevel.logDebug)
                    {
                        TinyLogger.Log("SpawnObjects sceneID:" + tni.sceneID + " name:" + tni.gameObject.name);
                    }

                    tni.gameObject.SetActive(true);
                }

                foreach (var tni2 in tnis)
                {
                    if (tni2.gameObject.hideFlags == HideFlags.NotEditable || tni2.gameObject.hideFlags == HideFlags.HideAndDontSave)
                    {
                        continue;
                    }

                    // If not a scene object
                    if (tni2.sceneID == 0)
                    {
                        continue;
                    }

                    // What does this mean???
                    if (tni2.isServer)
                    {
                        continue;
                    }

                    if (tni2.gameObject == null)
                    {
                        if (TinyNetLogLevel.logError)
                        {
                            TinyLogger.LogError("Log this? Something is wrong if this happens?");
                        }
                        continue;
                    }

                    SpawnObject(tni2.gameObject);

                    // these objects are server authority - even if "localPlayerAuthority" is set on them
                    tni2.ForceAuthority(true);
                }
            }

            return(true);
        }
        //============ Clients Functions ====================//

        /// <summary>
        /// Sets the client as ready.
        /// </summary>
        /// <param name="conn">The connection.</param>
        void SetClientReady(TinyNetConnection conn)
        {
            if (TinyNetLogLevel.logDebug)
            {
                TinyLogger.Log("SetClientReady for conn:" + conn.ConnectId);
            }

            if (conn.isReady)
            {
                if (TinyNetLogLevel.logDebug)
                {
                    TinyLogger.Log("SetClientReady conn " + conn.ConnectId + " already ready");
                }
                return;
            }

            if (conn.playerControllers.Count == 0)
            {
                if (TinyNetLogLevel.logDebug)
                {
                    TinyLogger.LogWarning("Ready with no player object");
                }
            }

            conn.isReady = true;

            // This is only in case this is a listen server.
            TinyNetLocalConnectionToClient localConnection = conn as TinyNetLocalConnectionToClient;

            if (localConnection != null)
            {
                if (TinyNetLogLevel.logDev)
                {
                    TinyLogger.Log("NetworkServer Ready handling TinyNetLocalConnectionToClient");
                }

                // Setup spawned objects for local player
                // Only handle the local objects for the first player (no need to redo it when doing more local players)
                // and don't handle player objects here, they were done above
                foreach (TinyNetIdentity tinyNetId in LocalIdentityObjects.Values)
                {
                    // Need to call OnStartClient directly here, as it's already been added to the local object dictionary
                    // in the above SetLocalPlayer call
                    if (tinyNetId != null && tinyNetId.gameObject != null)
                    {
                        if (!tinyNetId.isClient)
                        {
                            localConnection.ShowObjectToConnection(tinyNetId);

                            if (TinyNetLogLevel.logDev)
                            {
                                TinyLogger.Log("LocalClient.SetSpawnObject calling OnStartClient");
                            }
                            tinyNetId.OnStartClient();
                        }
                    }
                }

                return;
            }

            // Spawn/update all current server objects
            if (TinyNetLogLevel.logDebug)
            {
                TinyLogger.Log("Spawning " + LocalIdentityObjects.Count + " objects for conn " + conn.ConnectId);
            }

            TinyNetObjectSpawnFinishedMessage msg = new TinyNetObjectSpawnFinishedMessage();

            msg.state = 0;             //State 0 means we are starting the spawn messages 'spam'.
            SendMessageByChannelToTargetConnection(msg, DeliveryMethod.ReliableOrdered, conn);

            foreach (TinyNetIdentity tinyNetId in LocalIdentityObjects.Values)
            {
                if (tinyNetId == null)
                {
                    if (TinyNetLogLevel.logWarn)
                    {
                        TinyLogger.LogWarning("Invalid object found in server local object list (null TinyNetIdentity).");
                    }
                    continue;
                }
                if (!tinyNetId.gameObject.activeSelf)
                {
                    continue;
                }

                if (TinyNetLogLevel.logDebug)
                {
                    TinyLogger.Log("Sending spawn message for current server objects name='" + tinyNetId.gameObject.name + "' netId=" + tinyNetId.TinyInstanceID);
                }

                conn.ShowObjectToConnection(tinyNetId);
            }

            if (TinyNetLogLevel.logDebug)
            {
                TinyLogger.Log("Spawning objects for conn " + conn.ConnectId + " finished");
            }

            msg.state = 1;             //We finished spamming the spawn messages!
            SendMessageByChannelToTargetConnection(msg, DeliveryMethod.ReliableOrdered, conn);
        }