/// <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); } } }
/// <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); }
/// <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"); } }
/// <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); } } }
/// <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(); } }
/// <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); }