internal static BitStream WrapMessage(byte messageType, ulong clientId, BitStream messageBody, SecuritySendFlags flags) { try { bool encrypted = ((flags & SecuritySendFlags.Encrypted) == SecuritySendFlags.Encrypted) && NetworkingManager.Singleton.NetworkConfig.EnableEncryption; bool authenticated = (flags & SecuritySendFlags.Authenticated) == SecuritySendFlags.Authenticated && NetworkingManager.Singleton.NetworkConfig.EnableEncryption; PooledBitStream outStream = PooledBitStream.Get(); using (PooledBitWriter outWriter = PooledBitWriter.Get(outStream)) { outWriter.WriteBit(encrypted); outWriter.WriteBit(authenticated); #if !DISABLE_CRYPTOGRAPHY if (authenticated || encrypted) { outWriter.WritePadBits(); long hmacWritePos = outStream.Position; if (authenticated) { outStream.Write(HMAC_PLACEHOLDER, 0, HMAC_PLACEHOLDER.Length); } if (encrypted) { using (RijndaelManaged rijndael = new RijndaelManaged()) { rijndael.GenerateIV(); rijndael.Padding = PaddingMode.PKCS7; byte[] key = NetworkingManager.Singleton.IsServer ? CryptographyHelper.GetClientKey(clientId) : CryptographyHelper.GetServerKey(); if (key == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError("Failed to grab key"); } return(null); } rijndael.Key = key; outStream.Write(rijndael.IV); using (CryptoStream encryptionStream = new CryptoStream(outStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write)) { encryptionStream.WriteByte(messageType); encryptionStream.Write(messageBody.GetBuffer(), 0, (int)messageBody.Length); } } } else { outStream.WriteByte(messageType); outStream.Write(messageBody.GetBuffer(), 0, (int)messageBody.Length); } if (authenticated) { byte[] key = NetworkingManager.Singleton.IsServer ? CryptographyHelper.GetClientKey(clientId) : CryptographyHelper.GetServerKey(); if (key == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError("Failed to grab key"); } return(null); } using (HMACSHA256 hmac = new HMACSHA256(key)) { byte[] computedHmac = hmac.ComputeHash(outStream.GetBuffer(), 0, (int)outStream.Length); outStream.Position = hmacWritePos; outStream.Write(computedHmac, 0, computedHmac.Length); } } } else { #endif outWriter.WriteBits(messageType, 6); outStream.Write(messageBody.GetBuffer(), 0, (int)messageBody.Length); #if !DISABLE_CRYPTOGRAPHY } #endif } return(outStream); } catch (Exception e) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogError("Error while wrapping headers"); } if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError(e.ToString()); } return(null); } }
private void Update() { if (!IsOwner) { return; } if (agent.destination != lastDestination) { lastDestination = agent.destination; using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteSinglePacked(agent.destination.x); writer.WriteSinglePacked(agent.destination.y); writer.WriteSinglePacked(agent.destination.z); writer.WriteSinglePacked(agent.velocity.x); writer.WriteSinglePacked(agent.velocity.y); writer.WriteSinglePacked(agent.velocity.z); writer.WriteSinglePacked(transform.position.x); writer.WriteSinglePacked(transform.position.y); writer.WriteSinglePacked(transform.position.z); if (!EnableProximity) { InvokeClientRpcOnEveryonePerformance(OnNavMeshStateUpdate, stream); } else { List <ulong> proximityClients = new List <ulong>(); foreach (KeyValuePair <ulong, NetworkedClient> client in NetworkingManager.Singleton.ConnectedClients) { if (client.Value.PlayerObject == null || Vector3.Distance(client.Value.PlayerObject.transform.position, transform.position) <= ProximityRange) { proximityClients.Add(client.Key); } } InvokeClientRpcPerformance(OnNavMeshStateUpdate, proximityClients, stream); } } } } if (NetworkingManager.Singleton.NetworkTime - lastCorrectionTime >= CorrectionDelay) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteSinglePacked(agent.velocity.x); writer.WriteSinglePacked(agent.velocity.y); writer.WriteSinglePacked(agent.velocity.z); writer.WriteSinglePacked(transform.position.x); writer.WriteSinglePacked(transform.position.y); writer.WriteSinglePacked(transform.position.z); if (!EnableProximity) { InvokeClientRpcOnEveryonePerformance(OnNavMeshCorrectionUpdate, stream); } else { List <ulong> proximityClients = new List <ulong>(); foreach (KeyValuePair <ulong, NetworkedClient> client in NetworkingManager.Singleton.ConnectedClients) { if (client.Value.PlayerObject == null || Vector3.Distance(client.Value.PlayerObject.transform.position, transform.position) <= ProximityRange) { proximityClients.Add(client.Key); } } InvokeClientRpcPerformance(OnNavMeshCorrectionUpdate, proximityClients, stream); } } } lastCorrectionTime = NetworkingManager.Singleton.NetworkTime; } }
internal static NetworkedObject CreateSpawnedObject(int networkedPrefabId, uint networkId, uint owner, bool playerObject, uint sceneSpawnedInIndex, bool sceneDelayedSpawn, bool destroyWithScene, Vector3?position, Quaternion?rotation, bool isActive, Stream stream, bool readPayload, int payloadLength, bool readNetworkedVar) { if (networkedPrefabId >= netManager.NetworkConfig.NetworkedPrefabs.Count || networkedPrefabId < 0) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Cannot spawn the object, invalid prefabIndex: " + networkedPrefabId); } return(null); } //Delayed spawning if (sceneDelayedSpawn && sceneSpawnedInIndex != NetworkSceneManager.CurrentActiveSceneIndex) { GameObject prefab = netManager.NetworkConfig.NetworkedPrefabs[networkedPrefabId].prefab; bool prefabActive = prefab.activeSelf; prefab.SetActive(false); GameObject go = (position == null && rotation == null) ? MonoBehaviour.Instantiate(prefab) : MonoBehaviour.Instantiate(prefab, position.GetValueOrDefault(Vector3.zero), rotation.GetValueOrDefault(Quaternion.identity)); prefab.SetActive(prefabActive); //Appearantly some wierd behavior when switching scenes can occur that destroys this object even though the scene is //not destroyed, therefor we set it to DontDestroyOnLoad here, to prevent that problem. MonoBehaviour.DontDestroyOnLoad(go); NetworkedObject netObject = go.GetComponent <NetworkedObject>(); if (netObject == null) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Please add a NetworkedObject component to the root of all spawnable objects"); } netObject = go.AddComponent <NetworkedObject>(); } netObject.NetworkedPrefabName = netManager.NetworkConfig.NetworkedPrefabs[networkedPrefabId].name; netObject.IsSpawned = false; netObject.IsPooledObject = false; if (netManager.IsServer) { netObject.NetworkId = GetNetworkObjectId(); } else { netObject.NetworkId = networkId; } netObject.destroyWithScene = destroyWithScene; netObject.OwnerClientId = owner; netObject.IsPlayerObject = playerObject; netObject.SceneDelayedSpawn = sceneDelayedSpawn; netObject.sceneSpawnedInIndex = sceneSpawnedInIndex; Dictionary <ushort, List <INetworkedVar> > dummyNetworkedVars = new Dictionary <ushort, List <INetworkedVar> >(); List <NetworkedBehaviour> networkedBehaviours = new List <NetworkedBehaviour>(netObject.GetComponentsInChildren <NetworkedBehaviour>()); for (ushort i = 0; i < networkedBehaviours.Count; i++) { dummyNetworkedVars.Add(i, networkedBehaviours[i].GetDummyNetworkedVars()); } PendingSpawnObject pso = new PendingSpawnObject() { netObject = netObject, dummyNetworkedVars = dummyNetworkedVars, sceneSpawnedInIndex = sceneSpawnedInIndex, playerObject = playerObject, owner = owner, isActive = isActive, payload = null }; PendingSpawnObjects.Add(netObject.NetworkId, pso); pso.SetNetworkedVarData(stream); if (readPayload) { MLAPI.Serialization.BitStream payloadStream = new MLAPI.Serialization.BitStream(); payloadStream.CopyUnreadFrom(stream, payloadLength); stream.Position += payloadLength; pso.payload = payloadStream; } return(netObject); } //Normal spawning { GameObject prefab = netManager.NetworkConfig.NetworkedPrefabs[networkedPrefabId].prefab; GameObject go = (position == null && rotation == null) ? MonoBehaviour.Instantiate(prefab) : MonoBehaviour.Instantiate(prefab, position.GetValueOrDefault(Vector3.zero), rotation.GetValueOrDefault(Quaternion.identity)); NetworkedObject netObject = go.GetComponent <NetworkedObject>(); if (netObject == null) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Please add a NetworkedObject component to the root of all spawnable objects"); } netObject = go.AddComponent <NetworkedObject>(); } if (readNetworkedVar) { netObject.SetNetworkedVarData(stream); } netObject.NetworkedPrefabName = netManager.NetworkConfig.NetworkedPrefabs[networkedPrefabId].name; netObject.IsSpawned = true; netObject.IsPooledObject = false; if (netManager.IsServer) { netObject.NetworkId = GetNetworkObjectId(); } else { netObject.NetworkId = networkId; } netObject.destroyWithScene = destroyWithScene; netObject.OwnerClientId = owner; netObject.IsPlayerObject = playerObject; netObject.SceneDelayedSpawn = sceneDelayedSpawn; netObject.sceneSpawnedInIndex = sceneSpawnedInIndex; SpawnedObjects.Add(netObject.NetworkId, netObject); SpawnedObjectsList.Add(netObject); if (playerObject) { NetworkingManager.Singleton.ConnectedClients[owner].PlayerObject = netObject; } if (readPayload) { using (PooledBitStream payloadStream = PooledBitStream.Get()) { payloadStream.CopyUnreadFrom(stream, payloadLength); stream.Position += payloadLength; netObject.InvokeBehaviourNetworkSpawn(payloadStream); } } else { netObject.InvokeBehaviourNetworkSpawn(null); } netObject.gameObject.SetActive(isActive); return(netObject); } }
// This method is responsible for unwrapping a message, that is extracting the messagebody. // Could include decrypting and/or authentication. internal static BitStream UnwrapMessage(BitStream inputStream, ulong clientId, out byte messageType, out SecuritySendFlags security) { using (PooledBitReader inputHeaderReader = PooledBitReader.Get(inputStream)) { try { if (inputStream.Length < 1) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogError("The incoming message was too small"); } messageType = MLAPIConstants.INVALID; security = SecuritySendFlags.None; return(null); } bool isEncrypted = inputHeaderReader.ReadBit(); bool isAuthenticated = inputHeaderReader.ReadBit(); if (isEncrypted && isAuthenticated) { security = SecuritySendFlags.Encrypted | SecuritySendFlags.Authenticated; } else if (isEncrypted) { security = SecuritySendFlags.Encrypted; } else if (isAuthenticated) { security = SecuritySendFlags.Authenticated; } else { security = SecuritySendFlags.None; } #if !DISABLE_CRYPTOGRAPHY if (isEncrypted || isAuthenticated) { if (!NetworkingManager.Singleton.NetworkConfig.EnableEncryption) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError("Got a encrypted and/or authenticated message but key exchange (\"encryption\") was not enabled"); } messageType = MLAPIConstants.INVALID; return(null); } // Skip last bits in first byte inputHeaderReader.SkipPadBits(); if (isAuthenticated) { long hmacStartPos = inputStream.Position; int readHmacLength = inputStream.Read(HMAC_BUFFER, 0, HMAC_BUFFER.Length); if (readHmacLength != HMAC_BUFFER.Length) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError("HMAC length was invalid"); } messageType = MLAPIConstants.INVALID; return(null); } // Now we have read the HMAC, we need to set the hmac in the input to 0s to perform the HMAC. inputStream.Position = hmacStartPos; inputStream.Write(HMAC_PLACEHOLDER, 0, HMAC_PLACEHOLDER.Length); byte[] key = NetworkingManager.Singleton.IsServer ? CryptographyHelper.GetClientKey(clientId) : CryptographyHelper.GetServerKey(); if (key == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError("Failed to grab key"); } messageType = MLAPIConstants.INVALID; return(null); } using (HMACSHA256 hmac = new HMACSHA256(key)) { byte[] computedHmac = hmac.ComputeHash(inputStream.GetBuffer(), 0, (int)inputStream.Length); if (!CryptographyHelper.ConstTimeArrayEqual(computedHmac, HMAC_BUFFER)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError("Received HMAC did not match the computed HMAC"); } messageType = MLAPIConstants.INVALID; return(null); } } } if (isEncrypted) { int ivRead = inputStream.Read(IV_BUFFER, 0, IV_BUFFER.Length); if (ivRead != IV_BUFFER.Length) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogError("Invalid IV size"); } messageType = MLAPIConstants.INVALID; return(null); } PooledBitStream outputStream = PooledBitStream.Get(); using (RijndaelManaged rijndael = new RijndaelManaged()) { rijndael.IV = IV_BUFFER; rijndael.Padding = PaddingMode.PKCS7; byte[] key = NetworkingManager.Singleton.IsServer ? CryptographyHelper.GetClientKey(clientId) : CryptographyHelper.GetServerKey(); if (key == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError("Failed to grab key"); } messageType = MLAPIConstants.INVALID; return(null); } rijndael.Key = key; using (CryptoStream cryptoStream = new CryptoStream(outputStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write)) { cryptoStream.Write(inputStream.GetBuffer(), (int)inputStream.Position, (int)(inputStream.Length - inputStream.Position)); } outputStream.Position = 0; if (outputStream.Length == 0) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogError("The incoming message was too small"); } messageType = MLAPIConstants.INVALID; return(null); } int msgType = outputStream.ReadByte(); messageType = msgType == -1 ? MLAPIConstants.INVALID : (byte)msgType; } return(outputStream); } else { if (inputStream.Length - inputStream.Position <= 0) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogError("The incoming message was too small"); } messageType = MLAPIConstants.INVALID; return(null); } int msgType = inputStream.ReadByte(); messageType = msgType == -1 ? MLAPIConstants.INVALID : (byte)msgType; return(inputStream); } } else { #endif messageType = inputHeaderReader.ReadByteBits(6); // The input stream is now ready to be read from. It's "safe" and has the correct position return(inputStream); #if !DISABLE_CRYPTOGRAPHY } #endif } catch (Exception e) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogError("Error while unwrapping headers"); } if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogError(e.ToString()); } security = SecuritySendFlags.None; messageType = MLAPIConstants.INVALID; return(null); } } }
private static void OnSceneUnloadClient(Guid switchSceneGuid, Stream objectStream) { if (NetworkingManager.Singleton.NetworkConfig.UsePrefabSync) { SpawnManager.DestroySceneObjects(); using (PooledBitReader reader = PooledBitReader.Get(objectStream)) { uint newObjectsCount = reader.ReadUInt32Packed(); for (int i = 0; i < newObjectsCount; i++) { bool isPlayerObject = reader.ReadBool(); ulong networkId = reader.ReadUInt64Packed(); ulong owner = reader.ReadUInt64Packed(); ulong prefabHash = reader.ReadUInt64Packed(); bool destroyWithScene = reader.ReadBool(); Vector3 position = new Vector3(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked()); Quaternion rotation = Quaternion.Euler(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked()); NetworkedObject networkedObject = SpawnManager.CreateLocalNetworkedObject(false, 0, prefabHash, position, rotation); SpawnManager.SpawnNetworkedObjectLocally(networkedObject, networkId, true, isPlayerObject, owner, objectStream, false, 0, true, destroyWithScene); } } } else { NetworkedObject[] networkedObjects = MonoBehaviour.FindObjectsOfType <NetworkedObject>(); SpawnManager.ClientCollectSoftSyncSceneObjectSweep(networkedObjects); using (PooledBitReader reader = PooledBitReader.Get(objectStream)) { uint newObjectsCount = reader.ReadUInt32Packed(); for (int i = 0; i < newObjectsCount; i++) { bool isPlayerObject = reader.ReadBool(); ulong networkId = reader.ReadUInt64Packed(); ulong owner = reader.ReadUInt64Packed(); ulong instanceId = reader.ReadUInt64Packed(); bool destroyWithScene = reader.ReadBool(); NetworkedObject networkedObject = SpawnManager.CreateLocalNetworkedObject(true, instanceId, 0, null, null); SpawnManager.SpawnNetworkedObjectLocally(networkedObject, networkId, true, isPlayerObject, owner, objectStream, false, 0, true, destroyWithScene); } } } using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteByteArray(switchSceneGuid.ToByteArray()); InternalMessageHandler.Send(NetworkingManager.Singleton.ServerClientId, MLAPIConstants.MLAPI_CLIENT_SWITCH_SCENE_COMPLETED, "MLAPI_INTERNAL", stream, SecuritySendFlags.None, null); } } isSwitching = false; if (OnSceneSwitched != null) { OnSceneSwitched(); } }
// Essentially, when this is passed an empty list, the okay will be sent to the server IEnumerator SendNeededFilesListToSender(List <int> _fileIDs) { bool allFilesReceived = _fileIDs.Count == 0; Debug.Log("sending needed files, files: " + _fileIDs.Count); PooledBitStream bitStream = PooledBitStream.Get(); PooledBitWriter writer = PooledBitWriter.Get(bitStream); if (allFilesReceived) { writer.WriteBit(true); writer.WriteIntArray(_fileIDs.ToArray()); writer.WriteBit(true); CustomMessagingManager.SendNamedMessage(MessageName, SenderID, bitStream, "MLAPI_INTERNAL"); bitStream.Dispose(); writer.Dispose(); _fileIDs.Clear(); StopListening(); if (OnDownloadComplete != null) { OnDownloadComplete(SendOrReceiveFlag.Receive, SenderID); } ChangeState(LargeRPCState.Idle); } else { var i = 0; List <int> ids = _fileIDs; foreach (var id in ids) { i++; bool isFinalPacket = i >= _fileIDs.Count; if (i >= _fileIDs.Count) { writer.WriteBit(isFinalPacket); writer.WriteIntArray(_fileIDs.ToArray()); writer.WriteBit(false); CustomMessagingManager.SendNamedMessage(MessageName, SenderID, bitStream, "MLAPI_INTERNAL"); bitStream.Dispose(); writer.Dispose(); bitStream = PooledBitStream.Get(); writer = PooledBitWriter.Get(bitStream); _fileIDs.Clear(); yield return(new WaitForSeconds(1 / 8)); break; } } } yield break; }
private static void OnSceneUnloadClient(Guid switchSceneGuid, Stream objectStream) { if (!NetworkingManager.Singleton.NetworkConfig.EnableSceneManagement || NetworkingManager.Singleton.NetworkConfig.UsePrefabSync) { SpawnManager.DestroySceneObjects(); using (PooledBitReader reader = PooledBitReader.Get(objectStream)) { uint newObjectsCount = reader.ReadUInt32Packed(); for (int i = 0; i < newObjectsCount; i++) { bool isPlayerObject = reader.ReadBool(); ulong networkId = reader.ReadUInt64Packed(); ulong owner = reader.ReadUInt64Packed(); bool hasParent = reader.ReadBool(); ulong?parentNetworkId = null; if (hasParent) { parentNetworkId = reader.ReadUInt64Packed(); } ulong prefabHash = reader.ReadUInt64Packed(); Vector3? position = null; Quaternion?rotation = null; if (reader.ReadBool()) { position = new Vector3(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked()); rotation = Quaternion.Euler(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked()); } NetworkedObject networkedObject = SpawnManager.CreateLocalNetworkedObject(false, 0, prefabHash, parentNetworkId, position, rotation); SpawnManager.SpawnNetworkedObjectLocally(networkedObject, networkId, true, isPlayerObject, owner, objectStream, false, 0, true, false); Queue <BufferManager.BufferedMessage> bufferQueue = BufferManager.ConsumeBuffersForNetworkId(networkId); // Apply buffered messages if (bufferQueue != null) { while (bufferQueue.Count > 0) { BufferManager.BufferedMessage message = bufferQueue.Dequeue(); NetworkingManager.Singleton.HandleIncomingData(message.sender, message.channelName, new ArraySegment <byte>(message.payload.GetBuffer(), (int)message.payload.Position, (int)message.payload.Length), message.receiveTime, false); BufferManager.RecycleConsumedBufferedMessage(message); } } } } } else { NetworkedObject[] networkedObjects = MonoBehaviour.FindObjectsOfType <NetworkedObject>(); SpawnManager.ClientCollectSoftSyncSceneObjectSweep(networkedObjects); using (PooledBitReader reader = PooledBitReader.Get(objectStream)) { uint newObjectsCount = reader.ReadUInt32Packed(); for (int i = 0; i < newObjectsCount; i++) { bool isPlayerObject = reader.ReadBool(); ulong networkId = reader.ReadUInt64Packed(); ulong owner = reader.ReadUInt64Packed(); bool hasParent = reader.ReadBool(); ulong?parentNetworkId = null; if (hasParent) { parentNetworkId = reader.ReadUInt64Packed(); } ulong instanceId = reader.ReadUInt64Packed(); NetworkedObject networkedObject = SpawnManager.CreateLocalNetworkedObject(true, instanceId, 0, parentNetworkId, null, null); SpawnManager.SpawnNetworkedObjectLocally(networkedObject, networkId, true, isPlayerObject, owner, objectStream, false, 0, true, false); Queue <BufferManager.BufferedMessage> bufferQueue = BufferManager.ConsumeBuffersForNetworkId(networkId); // Apply buffered messages if (bufferQueue != null) { while (bufferQueue.Count > 0) { BufferManager.BufferedMessage message = bufferQueue.Dequeue(); NetworkingManager.Singleton.HandleIncomingData(message.sender, message.channelName, new ArraySegment <byte>(message.payload.GetBuffer(), (int)message.payload.Position, (int)message.payload.Length), message.receiveTime, false); BufferManager.RecycleConsumedBufferedMessage(message); } } } } } using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteByteArray(switchSceneGuid.ToByteArray()); NetworkedObject networkedObject = null; InternalMessageSender.Send(NetworkingManager.Singleton.ServerClientId, MLAPIConstants.MLAPI_CLIENT_SWITCH_SCENE_COMPLETED, "MLAPI_INTERNAL", stream, SecuritySendFlags.None, networkedObject); } } isSwitching = false; if (OnSceneSwitched != null) { OnSceneSwitched(); } }
internal void Tick(uint tick) { if (IsServer) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteUInt32(tick); Dictionary <ulong, IEntityState> states = new Dictionary <ulong, IEntityState>(entities.Count); foreach (Entity entity in entities.Values) { IEntityState state = entity.DoStateTick(tick); if (state != null) { states.Add(entity.NetworkId, state); } } writer.WriteInt32(states.Count); foreach (var state in states) { writer.WriteUInt64(state.Key); state.Value.Serialize(writer); } CustomMessagingManager.SendNamedMessage("NetworkState", null, stream); } } } else { foreach (Entity entity in entities.Values) { entity.DoStateTick(tick); } uint predictTicks = (uint)Math.Ceiling(RTT / FixedStep) + 2 * Deviation; for (uint i = 0; i < predictTicks; i++) { if (tick + i > LastPredictedTick) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { LastPredictedTick = tick + i; var authEntities = entities.Values.Where(e => e.IsOwner); int count = authEntities.Count(); Dictionary <ulong, IEntityInput> inputs = new Dictionary <ulong, IEntityInput>(count); foreach (Entity entity in authEntities) { IEntityInput input = entity.DoInputTick(tick + i); if (input != null) { inputs.Add(entity.NetworkId, input); } } writer.WriteUInt32(tick + i); writer.WriteInt32(inputs.Count); foreach (var input in inputs) { writer.WriteUInt64(input.Key); input.Value.Serialize(writer); } CustomMessagingManager.SendNamedMessage("NetworkInput", NetworkingManager.Singleton.ServerClientId, stream); } } } foreach (Entity entity in entities.Values.Where(e => e.extrapolate || e.IsOwner)) { entity.DoStateTick(tick + i); } } foreach (Entity entity in entities.Values) { entity.Present(); } } }
internal static void SpawnObject(NetworkedObject netObject, uint?clientOwnerId = null, Stream payload = null) { if (netObject.isSpawned) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Object already spawned"); } return; } else if (!netManager.isServer) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Only server can spawn objects"); } return; } else if (!netManager.NetworkConfig.NetworkPrefabIds.ContainsKey(netObject.NetworkedPrefabName)) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("The prefab name " + netObject.NetworkedPrefabName + " does not exist as a networkedPrefab"); } return; } else if (!netManager.NetworkConfig.HandleObjectSpawning) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("NetworkConfig is set to not handle object spawning"); } return; } uint netId = GetNetworkObjectId(); netObject.NetworkId = netId; SpawnedObjects.Add(netId, netObject); SpawnedObjectsList.Add(netObject); netObject.isSpawned = true; netObject.sceneObject = false; if (clientOwnerId != null) { netObject.OwnerClientId = clientOwnerId.Value; NetworkingManager.singleton.ConnectedClients[clientOwnerId.Value].OwnedObjects.Add(netObject); } if (payload == null) { netObject.InvokeBehaviourNetworkSpawn(null); } else { netObject.InvokeBehaviourNetworkSpawn(payload); } foreach (var client in netManager.ConnectedClients) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteBool(false); writer.WriteUInt32Packed(netObject.NetworkId); writer.WriteUInt32Packed(netObject.OwnerClientId); writer.WriteInt32Packed(netManager.NetworkConfig.NetworkPrefabIds[netObject.NetworkedPrefabName]); writer.WriteBool(netObject.sceneObject == null ? true : netObject.sceneObject.Value); writer.WriteSinglePacked(netObject.transform.position.x); writer.WriteSinglePacked(netObject.transform.position.y); writer.WriteSinglePacked(netObject.transform.position.z); writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.x); writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.y); writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.z); writer.WriteBool(payload != null); netObject.WriteNetworkedVarData(stream, client.Key); if (payload != null) { stream.CopyFrom(payload); } InternalMessageHandler.Send(client.Key, MLAPIConstants.MLAPI_ADD_OBJECT, "MLAPI_INTERNAL", stream); } } } }
public void HandleSpawnMessage(ulong clientID, Stream stream, float receiveTime) { using (PooledBitReader reader = PooledBitReader.Get(stream)) { ulong networkID = reader.ReadUInt64Packed(); //Network ID ulong ownerID = reader.ReadUInt64Packed(); //Owner Type behaviourType = RPCTypeDefinition.GetTypeFromHash(reader.ReadUInt64Packed()); bool hasUniqueHash = reader.ReadBool(); ulong uniqueHash = 0; if (hasUniqueHash) { uniqueHash = reader.ReadUInt64Packed(); } bool ownerCanUnspawn = reader.ReadBool(); bool destroyOnUnspawn = reader.ReadBool(); //Read spawn payload PooledBitStream payloadStream = null; if (reader.ReadBool()) { payloadStream = PooledBitStream.Get(); int payloadLength = reader.ReadInt32Packed(); payloadStream.CopyUnreadFrom(stream, payloadLength); stream.Position += payloadLength; payloadStream.Position = 0; } if (networkManager.enableLogging) { string s = "Received add object event from server. Object: " + behaviourType.ToString() + " | Network ID: " + networkID + " | Owner: " + ownerID + " | Has Unique Hash: " + hasUniqueHash + " | "; if (hasUniqueHash) { s += uniqueHash + " | "; } s += "Owner Can Unspawn: " + ownerCanUnspawn + " | Destroy On Unspawn: " + destroyOnUnspawn; Debug.Log(s); } if (hasUniqueHash) { if (m_LocalPendingBehaviours.TryGetValue(uniqueHash, out PendingNetworkBehaviour pendingBehaviour)) { if (pendingBehaviour.reference.networkBehaviour.GetType() != behaviourType) { Debug.LogError("Received add object message where the remote network behaviour type(" + behaviourType.ToString() + ") does not match up with local network behaviour type (" + pendingBehaviour.reference.networkBehaviour.GetType() + ") with same unique ID(" + pendingBehaviour.reference.networkBehaviour.uniqueID + ").", pendingBehaviour.reference.networkBehaviour); return; } //Clean up pending m_LocalPendingBehaviours.Remove(uniqueHash); m_LocalPendingBehavioursList.Remove(pendingBehaviour.reference.networkBehaviour); OnObjectConnectSuccess(pendingBehaviour.reference, networkID, ownerID, ownerCanUnspawn, destroyOnUnspawn, payloadStream); if (payloadStream != null) { payloadStream.Dispose(); } } else if (m_RemotePendingBehavioursHashes.ContainsKey(uniqueHash)) { Debug.LogError("Received duplicate 'add object' message for hash '" + uniqueHash + "'."); return; } else if (m_RemotePendingBehaviours.ContainsKey(networkID)) { Debug.LogError("Recevied duplicate 'add object' message for network ID '" + networkID + "'."); return; } else { PendingNetworkBehaviour pendingBehaviourReference = new PendingNetworkBehaviour() { isRemoteBehaviour = true, uniqueHash = uniqueHash, ownerID = ownerID, networkID = networkID, ownerCanUnspawn = ownerCanUnspawn, destroyOnUnspawn = destroyOnUnspawn, spawnPayload = payloadStream }; m_RemotePendingBehavioursHashes.Add(uniqueHash, pendingBehaviourReference); m_RemotePendingBehaviours.Add(networkID, pendingBehaviourReference); } } else //No unique hash { //Build network behaviour GameObject behaviourObject = new GameObject("Server Network Object"); //All this stuff just in case the instantiate behaviour also instantiates other network behaviours in its awake function m_TrackAwakeSpawns = true; NetworkBehaviour serverBehaviour = (NetworkBehaviour)behaviourObject.AddComponent(behaviourType); m_TrackAwakeSpawns = false; if (m_BehavioursAwaitingSpawn.Count > 0) { while (m_BehavioursAwaitingSpawn.Count > 0) { NetworkBehaviourReference reference = m_BehavioursAwaitingSpawn.Dequeue(); if (reference.networkBehaviour == serverBehaviour) { OnObjectConnectSuccess(reference, networkID, ownerID, ownerCanUnspawn, destroyOnUnspawn, payloadStream); if (payloadStream != null) { payloadStream.Dispose(); } } else { SpawnOnNetworkClient(reference.networkBehaviour, reference.connectedClientCallback, reference.disconnectedDelegate, reference.localRPCDelegate, reference.ownerChangeDelegate); } } } if (!serverBehaviour.isNetworkSpawned) { serverBehaviour.uniqueID = "NETWORK_SERVER_BEHAVIOUR_" + networkID.ToString(); serverBehaviour.SpawnOnNetwork(); ulong hash = serverBehaviour.uniqueID.GetStableHash(networkManager.config.rpcHashSize); PendingNetworkBehaviour pendingBehaviour = m_LocalPendingBehaviours[hash]; //Clean up pending m_LocalPendingBehaviours.Remove(uniqueHash); m_LocalPendingBehavioursList.Remove(serverBehaviour); OnObjectConnectSuccess(pendingBehaviour.reference, networkID, ownerID, ownerCanUnspawn, destroyOnUnspawn, payloadStream); if (payloadStream != null) { payloadStream.Dispose(); } } } } }
internal void NetworkedVarUpdate() { if (!networkedVarInit) { NetworkedVarInit(); } //TODO: Do this efficiently. if (!CouldHaveDirtyVars()) { return; } networkedVarIndexesToReset.Clear(); networkedVarIndexesToResetSet.Clear(); for (int i = 0; i < NetworkingManager.singleton.ConnectedClientsList.Count; i++) { //This iterates over every "channel group". for (int j = 0; j < channelMappedVarIndexes.Count; j++) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteUInt32Packed(networkId); writer.WriteUInt16Packed(networkedObject.GetOrderIndex(this)); uint clientId = NetworkingManager.singleton.ConnectedClientsList[i].ClientId; bool writtenAny = false; for (int k = 0; k < networkedVarFields.Count; k++) { if (!channelMappedVarIndexes[j].Contains(k)) { //This var does not belong to the currently iterating channel group. writer.WriteBool(false); continue; } bool isDirty = networkedVarFields[k].IsDirty(); //cache this here. You never know what operations users will do in the dirty methods writer.WriteBool(isDirty); if (isDirty && (!isServer || networkedVarFields[k].CanClientRead(clientId))) { writtenAny = true; networkedVarFields[k].WriteDelta(stream); if (!networkedVarIndexesToResetSet.Contains(k)) { networkedVarIndexesToResetSet.Add(k); networkedVarIndexesToReset.Add(k); } } } if (writtenAny) { if (isServer) { InternalMessageHandler.Send(clientId, MLAPIConstants.MLAPI_NETWORKED_VAR_DELTA, channelsForVarGroups[j], stream, SecuritySendFlags.None); } else { InternalMessageHandler.Send(NetworkingManager.singleton.ServerClientId, MLAPIConstants.MLAPI_NETWORKED_VAR_DELTA, channelsForVarGroups[j], stream, SecuritySendFlags.None); } } } } } } for (int i = 0; i < networkedVarIndexesToReset.Count; i++) { networkedVarFields[networkedVarIndexesToReset[i]].ResetDirty(); } }
internal static void OnDestroyObject(uint networkId, bool destroyGameObject) { if ((netManager == null || !netManager.NetworkConfig.HandleObjectSpawning)) { return; } //Removal of pending object //Even though pending objects is marked with DontDestroyOnLoad, the OnDestroy method is invoked on pending objects. They are however not //destroyed (probably a unity bug for having an gameobject spawned as inactive). Therefore we only actual remove it from the list if //destroyGameObject is set to true, meaning MLAPI decided to destroy it, not unity. if (destroyGameObject == true && PendingSpawnObjects.ContainsKey(networkId)) { if (!PendingSpawnObjects[networkId].netObject.IsOwnedByServer && !PendingSpawnObjects[networkId].netObject.IsPlayerObject && netManager.ConnectedClients.ContainsKey(PendingSpawnObjects[networkId].netObject.OwnerClientId)) { //Someone owns it. for (int i = NetworkingManager.Singleton.ConnectedClients[PendingSpawnObjects[networkId].netObject.OwnerClientId].OwnedObjects.Count - 1; i > -1; i--) { if (NetworkingManager.Singleton.ConnectedClients[PendingSpawnObjects[networkId].netObject.OwnerClientId].OwnedObjects[i].NetworkId == networkId) { NetworkingManager.Singleton.ConnectedClients[PendingSpawnObjects[networkId].netObject.OwnerClientId].OwnedObjects.RemoveAt(i); } } } GameObject pendingGameObject = PendingSpawnObjects[networkId].netObject.gameObject; if (pendingGameObject != null) { MonoBehaviour.Destroy(pendingGameObject); } PendingSpawnObjects.Remove(networkId); } //Removal of spawned object if (!SpawnedObjects.ContainsKey(networkId)) { return; } if (!SpawnedObjects[networkId].IsOwnedByServer && !SpawnedObjects[networkId].IsPlayerObject && netManager.ConnectedClients.ContainsKey(SpawnedObjects[networkId].OwnerClientId)) { //Someone owns it. for (int i = NetworkingManager.Singleton.ConnectedClients[SpawnedObjects[networkId].OwnerClientId].OwnedObjects.Count - 1; i > -1; i--) { if (NetworkingManager.Singleton.ConnectedClients[SpawnedObjects[networkId].OwnerClientId].OwnedObjects[i].NetworkId == networkId) { NetworkingManager.Singleton.ConnectedClients[SpawnedObjects[networkId].OwnerClientId].OwnedObjects.RemoveAt(i); } } } SpawnedObjects[networkId].IsSpawned = false; if (netManager != null && netManager.IsServer) { releasedNetworkObjectIds.Push(networkId); if (SpawnedObjects[networkId] != null) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteUInt32Packed(networkId); InternalMessageHandler.Send(MLAPIConstants.MLAPI_DESTROY_OBJECT, "MLAPI_INTERNAL", stream, SecuritySendFlags.None); } } } } GameObject go = SpawnedObjects[networkId].gameObject; if (destroyGameObject && go != null) { MonoBehaviour.Destroy(go); } SpawnedObjects.Remove(networkId); for (int i = SpawnedObjectsList.Count - 1; i > -1; i--) { if (SpawnedObjectsList[i].NetworkId == networkId) { SpawnedObjectsList.RemoveAt(i); } } }
internal static void SpawnObject(NetworkedObject netObject, uint?clientOwnerId = null, Stream payload = null, bool destroyWithScene = false) { if (netObject.IsSpawned) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Object already spawned"); } return; } else if (!netManager.IsServer) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Only server can spawn objects"); } return; } else if (SpawnManager.GetNetworkedPrefabIndexOfName(netObject.NetworkedPrefabName) == -1) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("The prefab name " + netObject.NetworkedPrefabName + " does not exist as a networkedPrefab"); } return; } else if (!netManager.NetworkConfig.HandleObjectSpawning) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("NetworkConfig is set to not handle object spawning"); } return; } uint netId = GetNetworkObjectId(); netObject.NetworkId = netId; SpawnedObjects.Add(netId, netObject); SpawnedObjectsList.Add(netObject); netObject.IsSpawned = true; netObject.destroyWithScene = destroyWithScene; netObject.sceneSpawnedInIndex = NetworkSceneManager.CurrentActiveSceneIndex; if (clientOwnerId != null) { netObject.OwnerClientId = clientOwnerId.Value; NetworkingManager.Singleton.ConnectedClients[clientOwnerId.Value].OwnedObjects.Add(netObject); } if (payload == null) { netObject.InvokeBehaviourNetworkSpawn(null); } else { netObject.InvokeBehaviourNetworkSpawn(payload); } foreach (var client in netManager.ConnectedClients) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteBool(false); writer.WriteUInt32Packed(netObject.NetworkId); writer.WriteUInt32Packed(netObject.OwnerClientId); writer.WriteUInt64Packed(netObject.NetworkedPrefabHash); writer.WriteBool(netObject.destroyWithScene == null ? true : netObject.destroyWithScene.Value); writer.WriteBool(netObject.SceneDelayedSpawn); writer.WriteUInt32Packed(netObject.sceneSpawnedInIndex); writer.WriteSinglePacked(netObject.transform.position.x); writer.WriteSinglePacked(netObject.transform.position.y); writer.WriteSinglePacked(netObject.transform.position.z); writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.x); writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.y); writer.WriteSinglePacked(netObject.transform.rotation.eulerAngles.z); writer.WriteBool(payload != null); if (payload != null) { writer.WriteInt32Packed((int)payload.Length); } netObject.WriteNetworkedVarData(stream, client.Key); if (payload != null) { stream.CopyFrom(payload); } InternalMessageHandler.Send(client.Key, MLAPIConstants.MLAPI_ADD_OBJECT, "MLAPI_INTERNAL", stream, SecuritySendFlags.None); } } } }
// Ran on both server and client internal static void SpawnNetworkedObjectLocally(NetworkedObject netObject, ulong networkId, bool sceneObject, bool playerObject, ulong?ownerClientId, Stream dataStream, bool readPayload, int payloadLength, bool readNetworkedVar, bool destroyWithScene) { if (netObject == null) { throw new ArgumentNullException(nameof(netObject), "Cannot spawn null object"); } if (netObject.IsSpawned) { throw new SpawnStateException("Object is already spawned"); } if (readNetworkedVar && NetworkingManager.Singleton.NetworkConfig.EnableNetworkedVar) { netObject.SetNetworkedVarData(dataStream); netObject.SetSyncedVarData(dataStream); } netObject.IsSpawned = true; netObject.IsSceneObject = sceneObject; netObject.NetworkId = networkId; netObject.DestroyWithScene = sceneObject || destroyWithScene; netObject._ownerClientId = ownerClientId; netObject.IsPlayerObject = playerObject; SpawnedObjects.Add(netObject.NetworkId, netObject); SpawnedObjectsList.Add(netObject); if (ownerClientId != null) { if (NetworkingManager.Singleton.IsServer) { if (playerObject) { NetworkingManager.Singleton.ConnectedClients[ownerClientId.Value].PlayerObject = netObject; } else { NetworkingManager.Singleton.ConnectedClients[ownerClientId.Value].OwnedObjects.Add(netObject); } } else if (playerObject && ownerClientId.Value == NetworkingManager.Singleton.LocalClientId) { NetworkingManager.Singleton.ConnectedClients[ownerClientId.Value].PlayerObject = netObject; } } if (NetworkingManager.Singleton.IsServer) { for (int i = 0; i < NetworkingManager.Singleton.ConnectedClientsList.Count; i++) { if (netObject.CheckObjectVisibility == null || netObject.CheckObjectVisibility(NetworkingManager.Singleton.ConnectedClientsList[i].ClientId)) { netObject.observers.Add(NetworkingManager.Singleton.ConnectedClientsList[i].ClientId); } } } netObject.ResetNetworkedStartInvoked(); if (readPayload) { using (PooledBitStream payloadStream = PooledBitStream.Get()) { payloadStream.CopyUnreadFrom(dataStream, payloadLength); dataStream.Position += payloadLength; payloadStream.Position = 0; netObject.InvokeBehaviourNetworkSpawn(payloadStream); } } else { netObject.InvokeBehaviourNetworkSpawn(null); } }
/// <summary> /// Changes the owner of the object. Can only be called from the server or the owner of this Network Behaviour. /// </summary> /// <param name="targetClientID">The new owner clientId</param> public void SetOwner(ulong targetClientID) { if (!networkManager.isRunning) { Debug.LogError("Cannot set ownership. The network is not running."); return; } if (!isServer) { if (isOwner && targetClientID == networkManager.serverID) { RemoveOwnership(); return; } throw new NotServerException("Only the server can call NetworkBehaviour.SetOwner to target anything but the server."); } if (!isNetworkSpawned) { throw new NetworkException("Cannot change ownership. This Network Behaviour is not spawned."); } if (!IsNetworkVisibleTo(targetClientID)) { throw new NetworkException("Cannot change ownership to a client that does not have visibility of this Network Behaviour."); } //Owner does not change if (targetClientID == ownerID) { return; } if (targetClientID == networkManager.serverID) { RemoveOwnership(); return; } if (isOwner) { m_OwnerClientID = targetClientID; OnLostOwnership(); } else //This may seem redundant but we want ownership changes to be set before the OnLostOwnership call { m_OwnerClientID = targetClientID; } //Send to all (not pending)observers using (PooledBitStream baseStream = PooledBitStream.Get()) { DoOwnershipWrite(baseStream, targetClientID); baseStream.PadStream(); using (BitStream stream = MessagePacker.WrapMessage(networkBehaviourManager.ownerChangeMessageType, 0, baseStream, SecuritySendFlags.None)) { using (HashSet <ulong> .Enumerator observers = GetObservers()) { while (observers.MoveNext()) { if (observers.Current == NetworkManager.Get().serverID) { continue; } NetworkManager.Get().transport.Send(observers.Current, new ArraySegment <byte>(stream.GetBuffer(), 0, (int)stream.Length), networkManager.networkInternalChannel); } } } } }
internal static void OnDestroyObject(ulong networkId, bool destroyGameObject) { if (NetworkingManager.Singleton == null) { return; } //Removal of spawned object if (!SpawnedObjects.ContainsKey(networkId)) { return; } if (!SpawnedObjects[networkId].IsOwnedByServer && !SpawnedObjects[networkId].IsPlayerObject && NetworkingManager.Singleton.ConnectedClients.ContainsKey(SpawnedObjects[networkId].OwnerClientId)) { //Someone owns it. for (int i = NetworkingManager.Singleton.ConnectedClients[SpawnedObjects[networkId].OwnerClientId].OwnedObjects.Count - 1; i > -1; i--) { if (NetworkingManager.Singleton.ConnectedClients[SpawnedObjects[networkId].OwnerClientId].OwnedObjects[i].NetworkId == networkId) { NetworkingManager.Singleton.ConnectedClients[SpawnedObjects[networkId].OwnerClientId].OwnedObjects.RemoveAt(i); } } } SpawnedObjects[networkId].IsSpawned = false; if (NetworkingManager.Singleton != null && NetworkingManager.Singleton.IsServer) { if (NetworkingManager.Singleton.NetworkConfig.RecycleNetworkIds) { releasedNetworkObjectIds.Enqueue(new ReleasedNetworkId() { NetworkId = networkId, ReleaseTime = Time.unscaledTime }); } if (SpawnedObjects[networkId] != null) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteUInt64Packed(networkId); InternalMessageSender.Send(MLAPIConstants.MLAPI_DESTROY_OBJECT, "MLAPI_INTERNAL", stream, SecuritySendFlags.None, SpawnedObjects[networkId]); } } } } GameObject go = SpawnedObjects[networkId].gameObject; if (destroyGameObject && go != null) { if (customDestroyHandlers.ContainsKey(SpawnedObjects[networkId].PrefabHash)) { customDestroyHandlers[SpawnedObjects[networkId].PrefabHash](SpawnedObjects[networkId]); SpawnManager.OnDestroyObject(networkId, false); } else { MonoBehaviour.Destroy(go); } } SpawnedObjects.Remove(networkId); for (int i = SpawnedObjectsList.Count - 1; i > -1; i--) { if (SpawnedObjectsList[i].NetworkId == networkId) { SpawnedObjectsList.RemoveAt(i); } } }
private void Update() { if (isOwner) { if (NetworkingManager.singleton.NetworkTime - lastSendTime >= (1f / FixedSendsPerSecond) && (Vector3.Distance(transform.position, lastSentPos) > MinMeters || Quaternion.Angle(transform.rotation, lastSentRot) > MinDegrees)) { lastSendTime = NetworkingManager.singleton.NetworkTime; lastSentPos = transform.position; lastSentRot = transform.rotation; using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteSinglePacked(transform.position.x); writer.WriteSinglePacked(transform.position.y); writer.WriteSinglePacked(transform.position.z); writer.WriteSinglePacked(transform.rotation.eulerAngles.x); writer.WriteSinglePacked(transform.rotation.eulerAngles.y); writer.WriteSinglePacked(transform.rotation.eulerAngles.z); if (isServer) { InvokeClientRpcOnEveryoneExcept(ApplyTransform, OwnerClientId, stream); } else { InvokeServerRpc(SubmitTransform, stream); } } } } } else { //If we are server and interpolation is turned on for server OR we are not server and interpolation is turned on if ((isServer && InterpolateServer && InterpolatePosition) || (!isServer && InterpolatePosition)) { if (Vector3.Distance(transform.position, lerpEndPos) > SnapDistance) { //Snap, set T to 1 (100% of the lerp) lerpT = 1f; } float sendDelay = (isServer || !EnableRange || !AssumeSyncedSends) ? (1f / FixedSendsPerSecond) : GetTimeForLerp(transform.position, NetworkingManager.singleton.ConnectedClients[NetworkingManager.singleton.LocalClientId].PlayerObject.transform.position); lerpT += Time.time / sendDelay; if (ExtrapolatePosition && Time.time - lastRecieveTime < sendDelay * MaxSendsToExtrapolate) { transform.position = Vector3.LerpUnclamped(lerpStartPos, lerpEndPos, lerpT); } else { transform.position = Vector3.Lerp(lerpStartPos, lerpEndPos, lerpT); } if (ExtrapolatePosition && Time.time - lastRecieveTime < sendDelay * MaxSendsToExtrapolate) { transform.rotation = Quaternion.SlerpUnclamped(lerpStartRot, lerpEndRot, lerpT); } else { transform.rotation = Quaternion.Slerp(lerpStartRot, lerpEndRot, lerpT); } } } if (isServer && EnableRange && EnableNonProvokedResendChecks) { CheckForMissedSends(); } }
// Runs on client internal static void HandleHailRequest(ulong clientId, Stream stream) { X509Certificate2 certificate = null; byte[] serverDiffieHellmanPublicPart = null; using (PooledBitReader reader = PooledBitReader.Get(stream)) { if (NetworkingManager.Singleton.NetworkConfig.EnableEncryption) { // Read the certificate if (NetworkingManager.Singleton.NetworkConfig.SignKeyExchange) { // Allocation justification: This runs on client and only once, at initial connection certificate = new X509Certificate2(reader.ReadByteArray()); if (CryptographyHelper.VerifyCertificate(certificate, NetworkingManager.Singleton.ConnectedHostname)) { // The certificate is not valid :( // Man in the middle. if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid certificate. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } else { NetworkingManager.Singleton.NetworkConfig.ServerX509Certificate = certificate; } } // Read the ECDH // Allocation justification: This runs on client and only once, at initial connection serverDiffieHellmanPublicPart = reader.ReadByteArray(); // Verify the key exchange if (NetworkingManager.Singleton.NetworkConfig.SignKeyExchange) { int signatureType = reader.ReadByte(); byte[] serverDiffieHellmanPublicPartSignature = reader.ReadByteArray(); if (signatureType == 0) { RSACryptoServiceProvider rsa = certificate.PublicKey.Key as RSACryptoServiceProvider; if (rsa != null) { using (SHA256Managed sha = new SHA256Managed()) { if (!rsa.VerifyData(serverDiffieHellmanPublicPart, sha, serverDiffieHellmanPublicPartSignature)) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid RSA signature. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } } else { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("No RSA key found in certificate. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } else if (signatureType == 1) { DSACryptoServiceProvider dsa = certificate.PublicKey.Key as DSACryptoServiceProvider; if (dsa != null) { using (SHA256Managed sha = new SHA256Managed()) { if (!dsa.VerifyData(sha.ComputeHash(serverDiffieHellmanPublicPart), serverDiffieHellmanPublicPartSignature)) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid DSA signature. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } } else { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("No DSA key found in certificate. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } else { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Invalid signature type. Disconnecting"); } NetworkingManager.Singleton.StopClient(); return; } } } } using (PooledBitStream outStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(outStream)) { if (NetworkingManager.Singleton.NetworkConfig.EnableEncryption) { // Create a ECDH key EllipticDiffieHellman diffieHellman = new EllipticDiffieHellman(EllipticDiffieHellman.DEFAULT_CURVE, EllipticDiffieHellman.DEFAULT_GENERATOR, EllipticDiffieHellman.DEFAULT_ORDER); NetworkingManager.Singleton.clientAesKey = diffieHellman.GetSharedSecret(serverDiffieHellmanPublicPart); byte[] diffieHellmanPublicKey = diffieHellman.GetPublicKey(); writer.WriteByteArray(diffieHellmanPublicKey); } } // Send HailResponse InternalMessageSender.Send(NetworkingManager.Singleton.ServerClientId, MLAPIConstants.MLAPI_CERTIFICATE_HAIL_RESPONSE, "MLAPI_INTERNAL", outStream, SecuritySendFlags.None, null); } }
private void SubmitTransform(uint clientId, Stream stream) { if (!enabled) { return; } using (PooledBitReader reader = PooledBitReader.Get(stream)) { float xPos = reader.ReadSinglePacked(); float yPos = reader.ReadSinglePacked(); float zPos = reader.ReadSinglePacked(); float xRot = reader.ReadSinglePacked(); float yRot = reader.ReadSinglePacked(); float zRot = reader.ReadSinglePacked(); if (IsMoveValidDelegate != null && !IsMoveValidDelegate(lerpEndPos, new Vector3(xPos, yPos, zPos))) { //Invalid move! //TODO: Add rubber band (just a message telling them to go back) return; } using (PooledBitStream writeStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(writeStream)) { writer.WriteSinglePacked(xPos); writer.WriteSinglePacked(yPos); writer.WriteSinglePacked(zPos); writer.WriteSinglePacked(xRot); writer.WriteSinglePacked(yRot); writer.WriteSinglePacked(zRot); if (EnableRange) { for (int i = 0; i < NetworkingManager.singleton.ConnectedClientsList.Count; i++) { if (!clientSendInfo.ContainsKey(NetworkingManager.singleton.ConnectedClientsList[i].ClientId)) { clientSendInfo.Add(NetworkingManager.singleton.ConnectedClientsList[i].ClientId, new ClientSendInfo() { clientId = NetworkingManager.singleton.ConnectedClientsList[i].ClientId, lastMissedPosition = null, lastMissedRotation = null, lastSent = 0 }); } ClientSendInfo info = clientSendInfo[NetworkingManager.singleton.ConnectedClientsList[i].ClientId]; Vector3 receiverPosition = NetworkingManager.singleton.ConnectedClientsList[i].PlayerObject.transform.position; Vector3 senderPosition = NetworkingManager.singleton.ConnectedClients[OwnerClientId].PlayerObject.transform.position; if (NetworkingManager.singleton.NetworkTime - info.lastSent >= GetTimeForLerp(receiverPosition, senderPosition)) { info.lastSent = NetworkingManager.singleton.NetworkTime; info.lastMissedPosition = null; info.lastMissedRotation = null; InvokeClientRpcOnClient(ApplyTransform, NetworkingManager.singleton.ConnectedClientsList[i].ClientId, writeStream); } else { info.lastMissedPosition = new Vector3(xPos, yPos, zPos); info.lastMissedRotation = Quaternion.Euler(xRot, yRot, zRot); } } } else { InvokeClientRpcOnEveryoneExcept(ApplyTransform, OwnerClientId, writeStream); } } } } }
/// <summary> /// this function splits FILES into MEMORY SAFE sized chunks and safely sends one before starting another /// /// files receipient needs to receive the same number of headers with each header packet (packet 1 counts as a header packet) /// </summary> public IEnumerator SendFilesDownloadRoutine(string[] _paths, ulong _clientID) { Debug.Log("coroutine started"); if (State != LargeRPCState.Idle) { Debug.LogWarning("Cannot start sending files while files are being sent, waiting for Idle state to begin"); yield break; } ReceiverID = _clientID; ChangeState(LargeRPCState.Send_SendingHeaders); #region comment -- header sizes /* -- Header sizes -- * packet 1 * int fileCount 4b | long downloadSize 8b | <start headers> * * header packets * int fileID 4b | string filename varsize | byte[256] hash 32b | long fileLength 8b | bool isLastInPacket 1bit * * subsequent packets * int fileID 4b | int filedata_length 4b | byte[var] filedata <=netChunkSize | bool isLastInPacket 1byte */ #endregion #region Grab Download Information // grab info for headers foreach (var path in _paths) { if (File.Exists(path)) { using (FileStream fs = File.Open(path, FileMode.Open)) { Debug.Log(fs.Name); int id = Headers.Count; byte[] fileHash = fs.sha256(); yield return(new WaitForEndOfFrame()); FileHeader header = new FileHeader(id, Path.GetFileName(path), fileHash, fs.Length); Headers.Add(header); DownloadSize += header.fileSize; } // let it exit if it needs to, giving StopRoutine() a chance yield return(0); } else { Debug.LogWarning("File not found, skipping: " + path); } } #endregion #region send headers PooledBitStream bitStream = PooledBitStream.Get(); PooledBitWriter writer = PooledBitWriter.Get(bitStream); // fileCount writer.WriteInt32(Headers.Count); // downloadSize writer.WriteInt64(DownloadSize); var headersThisPacket = 0; var packetsSent = 0; Debug.Log("Sending headers"); foreach (var header in Headers) { // let it exit if it needs to, giving StopRoutine() a chance yield return(0); Debug.Log(headersThisPacket + " " + packetsSent); var path = header.path; var id = header.id; headersThisPacket++; // fileID writer.WriteInt32(header.id); // filename writer.WriteString(path); Debug.Log(Encoding.Unicode.GetString(header.hash)); // hash writer.WriteByteArray(header.hash, 32); // fileLength writer.WriteInt64(header.fileSize); bool isLastPacket = id >= Headers.Count - 1; // send it off if we've filled up a packet if (headersThisPacket >= headersPerPacket || isLastPacket) { Debug.Log("message going out"); // isLastInPacket writer.WriteBit(true); CustomMessagingManager.SendNamedMessage(MessageName, _clientID, bitStream, "MLAPI_INTERNAL"); /* headers are pretty small, they really don't need the receiver to check in here unless it becomes a problem * * // if we haven't sent any packets yet when we get here, wait for an okay from the receiver * if (packetsSent == 0) * { * ChangeState(LargeRPCState.Send_AwaitingOkayToSend); * * while (State == LargeRPCState.Send_AwaitingOkayToSend) * { * yield return new WaitForSeconds(0.5f); * } * }*/ packetsSent++; Debug.Log("headers: " + headersThisPacket + " packets: " + packetsSent); headersThisPacket = 0; writer.Dispose(); bitStream.Dispose(); bitStream = PooledBitStream.Get(); writer = PooledBitWriter.Get(bitStream); // don't wait on the last one if (!isLastPacket) { yield return(new WaitForSeconds(1 / 14)); } } else { writer.WriteBit(false); } } writer.Dispose(); bitStream.Dispose(); #endregion ChangeState(LargeRPCState.Send_AwaitingFilesNeededList); ListenForFilesNeededListOrCompletion(); // loop start while (State != LargeRPCState.Complete) { // let it exit if it needs to, giving StopRoutine() a chance yield return(0); Debug.Log("Not done, running not-complete loop"); #region wait for needed files list while (State == LargeRPCState.Send_AwaitingFilesNeededList || State == LargeRPCState.Send_EnsuringIntegrity) { Debug.Log("waiting for list"); yield return(new WaitForSeconds(0.5f)); } Debug.Log("No longer waiting for list"); #endregion // runs ReceiveFilesNeededListFromReceiver, changes state to either Send_SendingFiles or Complete if (filesToSend.Count > 0) { Debug.Log("client still needs more files, sending"); #region send files bitStream = PooledBitStream.Get(); writer = PooledBitWriter.Get(bitStream); foreach (var header in Headers) { // let it exit if it needs to, giving StopRoutine() a chance yield return(0); Debug.Log("processing header"); if (File.Exists(header.path) && filesToSend.Contains(header.id)) { Debug.Log("file is needed"); using (FileStream fs = File.Open(header.path, FileMode.Open)) { // while loop pulled from fs.Read docs from microsoft, a little confusing to the glance but works and will be fast int numBytesToRead = (int)fs.Length; int numBytesRead = 0; while (numBytesToRead > 0) { Debug.Log("still bytes left"); int thisFileChunkSize = fileChunkSize; thisFileChunkSize = Mathf.Min(thisFileChunkSize, numBytesToRead); byte[] fileChunk = new byte[thisFileChunkSize]; // Read may return anything from 0 to numBytesToRead. int n = fs.Read(fileChunk, numBytesRead, thisFileChunkSize); foreach (byte[] netChunk in fileChunk.Slices(netChunkSize, false)) { Debug.Log("processing next chunk"); // fileID writer.WriteInt32(header.id); //writer.WriteInt32(netChunk.Length); Debug.Log("netchunk len: " + netChunk.Length); // filedata writer.WriteByteArray(netChunk); // isLastInPacket, need to add in its own size bool isLastInPacket = bitStream.Length + 1 >= netChunkSize || netChunk.Length < netChunkSize; writer.WriteBit(isLastInPacket); if (isLastInPacket) { CustomMessagingManager.SendNamedMessage(MessageName, _clientID, bitStream, "MLAPI_INTERNAL"); Debug.Log("packet sent"); yield return(new WaitForSeconds(1 / 14)); writer.Dispose(); bitStream.Dispose(); bitStream = PooledBitStream.Get(); writer = PooledBitWriter.Get(bitStream); } } // Break when the end of the file is reached. if (n == 0) { Debug.Log("end of file reached, this is a failsafe"); break; } numBytesRead += n; numBytesToRead -= n; } } } } Debug.Log("all headers processed"); filesToSend.Clear(); // just failsafing these, should be disposed of already writer.Dispose(); bitStream.Dispose(); #endregion ChangeState(LargeRPCState.Send_EnsuringIntegrity); } Debug.Log("Waiting before checking completion again"); yield return(new WaitForSeconds(1f)); } StopListening(); Debug.Log("files sent"); if (OnDownloadComplete != null) { OnDownloadComplete(SendOrReceiveFlag.Send, ReceiverID); } ChangeState(LargeRPCState.Idle); yield break; }
// Ran on server internal static void HandleHailResponse(uint clientId, Stream stream, int channelId) { if (!netManager.PendingClients.ContainsKey(clientId) || netManager.PendingClients[clientId].ConnectionState != PendingClient.State.PendingHail) { return; } if (!netManager.NetworkConfig.EnableEncryption) { return; } using (PooledBitReader reader = PooledBitReader.Get(stream)) { if (NetworkingManager.singleton.PendingClients[clientId].KeyExchange != null) { byte[] diffieHellmanPublic = reader.ReadByteArray(); netManager.PendingClients[clientId].AesKey = netManager.PendingClients[clientId].KeyExchange.GetSharedSecret(diffieHellmanPublic); if (netManager.NetworkConfig.SignKeyExchange) { byte[] diffieHellmanPublicSignature = reader.ReadByteArray(); X509Certificate2 certificate = netManager.NetworkConfig.ServerX509Certificate; RSACryptoServiceProvider rsa = certificate.PrivateKey as RSACryptoServiceProvider; if (rsa != null) { using (SHA256Managed sha = new SHA256Managed()) { byte[] clientHash = rsa.Decrypt(diffieHellmanPublicSignature, false); byte[] serverHash = sha.ComputeHash(diffieHellmanPublic); if (clientHash.Length != serverHash.Length) { //Man in the middle. if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Signature length doesnt match for the key exchange public part. Disconnecting"); } } netManager.DisconnectClient(clientId); return; } for (int i = 0; i < clientHash.Length; i++) { if (clientHash[i] != serverHash[i]) { //Man in the middle. if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Signature doesnt match for the key exchange public part. Disconnecting"); } } netManager.DisconnectClient(clientId); return; } } } } else { throw new CryptographicException("[MLAPI] Only RSA certificates are supported. No valid RSA key was found"); } } } } netManager.PendingClients[clientId].ConnectionState = PendingClient.State.PendingConnection; netManager.PendingClients[clientId].KeyExchange = null; // Give to GC // Send greetings, they have passed all the handshakes using (PooledBitStream outStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(outStream)) { writer.WriteInt64Packed(DateTime.Now.Ticks); // This serves no purpose. } InternalMessageHandler.Send(clientId, MLAPIConstants.MLAPI_GREETINGS, "MLAPI_INTERNAL", outStream, SecuritySendFlags.None, true); } }
private static void OnSceneUnloadServer(Guid switchSceneGuid) { // Justification: Rare alloc, could(should?) reuse List <NetworkedObject> newSceneObjects = new List <NetworkedObject>(); { NetworkedObject[] networkedObjects = MonoBehaviour.FindObjectsOfType <NetworkedObject>(); for (int i = 0; i < networkedObjects.Length; i++) { if (networkedObjects[i].IsSceneObject == null) { SpawnManager.SpawnNetworkedObjectLocally(networkedObjects[i], SpawnManager.GetNetworkObjectId(), true, false, null, null, false, 0, false, true); newSceneObjects.Add(networkedObjects[i]); } } } for (int j = 0; j < NetworkingManager.Singleton.ConnectedClientsList.Count; j++) { if (NetworkingManager.Singleton.ConnectedClientsList[j].ClientId != NetworkingManager.Singleton.ServerClientId) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteUInt32Packed(CurrentActiveSceneIndex); writer.WriteByteArray(switchSceneGuid.ToByteArray()); uint sceneObjectsToSpawn = 0; for (int i = 0; i < newSceneObjects.Count; i++) { if (newSceneObjects[i].observers.Contains(NetworkingManager.Singleton.ConnectedClientsList[j].ClientId)) { sceneObjectsToSpawn++; } } writer.WriteUInt32Packed(sceneObjectsToSpawn); for (int i = 0; i < newSceneObjects.Count; i++) { if (newSceneObjects[i].observers.Contains(NetworkingManager.Singleton.ConnectedClientsList[j].ClientId)) { if (NetworkingManager.Singleton.NetworkConfig.UsePrefabSync) { writer.WriteBool(newSceneObjects[i].IsPlayerObject); writer.WriteUInt64Packed(newSceneObjects[i].NetworkId); writer.WriteUInt64Packed(newSceneObjects[i].OwnerClientId); writer.WriteUInt64Packed(newSceneObjects[i].PrefabHash); writer.WriteSinglePacked(newSceneObjects[i].transform.position.x); writer.WriteSinglePacked(newSceneObjects[i].transform.position.y); writer.WriteSinglePacked(newSceneObjects[i].transform.position.z); writer.WriteSinglePacked(newSceneObjects[i].transform.rotation.eulerAngles.x); writer.WriteSinglePacked(newSceneObjects[i].transform.rotation.eulerAngles.y); writer.WriteSinglePacked(newSceneObjects[i].transform.rotation.eulerAngles.z); if (NetworkingManager.Singleton.NetworkConfig.EnableNetworkedVar) { newSceneObjects[i].WriteNetworkedVarData(stream, NetworkingManager.Singleton.ConnectedClientsList[j].ClientId); } } else { writer.WriteBool(newSceneObjects[i].IsPlayerObject); writer.WriteUInt64Packed(newSceneObjects[i].NetworkId); writer.WriteUInt64Packed(newSceneObjects[i].OwnerClientId); writer.WriteUInt64Packed(newSceneObjects[i].NetworkedInstanceId); if (NetworkingManager.Singleton.NetworkConfig.EnableNetworkedVar) { newSceneObjects[i].WriteNetworkedVarData(stream, NetworkingManager.Singleton.ConnectedClientsList[j].ClientId); } } } } } InternalMessageSender.Send(NetworkingManager.Singleton.ConnectedClientsList[j].ClientId, MLAPIConstants.MLAPI_SWITCH_SCENE, "MLAPI_INTERNAL", stream, SecuritySendFlags.None, null); } } } //Tell server that scene load is completed if (NetworkingManager.Singleton.IsHost) { OnClientSwitchSceneCompleted(NetworkingManager.Singleton.LocalClientId, switchSceneGuid); } isSwitching = false; if (OnSceneSwitched != null) { OnSceneSwitched(); } }
void InvokeApplyTransformOnEveryone(Vector3 position, Quaternion rotation, string channelName) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { if (TransformTypeToSync == TransformType.Transform2D) { if (SyncPosition && SyncRotation) { writer.WriteVector2Packed(position); writer.WriteSinglePacked(rotation.eulerAngles.z); } else if (SyncPosition) { writer.WriteVector2Packed(position); } else if (SyncRotation) { writer.WriteSinglePacked(rotation.eulerAngles.z); } } else if (TransformTypeToSync == TransformType.Transform3D) { if (SyncPosition && SyncRotation) { writer.WriteVector3Packed(position); if (FullRotation) { writer.WriteVector3Packed(rotation.eulerAngles); } else { writer.WriteSinglePacked(rotation.eulerAngles.y); } } else if (SyncPosition) { writer.WriteVector3Packed(position); } else if (SyncRotation) { if (FullRotation) { writer.WriteVector3Packed(rotation.eulerAngles); } else { writer.WriteSinglePacked(rotation.eulerAngles.y); } } } if (EnableMinDistanceBetweenClients) { Vector3?senderPosition = transform.position; for (int i = 0; i < NetworkingManager.Singleton.ConnectedClientsList.Count; i++) { Vector3?receiverPosition = NetworkingManager.Singleton.ConnectedClientsList[i].PlayerObject == null ? null : new Vector3?(NetworkingManager.Singleton.ConnectedClientsList[i].PlayerObject.transform.position); if (receiverPosition == null || senderPosition == null || Vector3.Distance((Vector3)senderPosition, (Vector3)receiverPosition) < MinDistanceBetweenClients) { InvokeClientRpcOnClientPerformance(TransformTypeToSync == TransformType.Transform2D ? "ApplyTransform2D" : "ApplyTransform", NetworkingManager.Singleton.ConnectedClientsList[i].ClientId, stream, channelName, Security.SecuritySendFlags.None); } } } else { InvokeClientRpcOnEveryonePerformance(TransformTypeToSync == TransformType.Transform2D ? "ApplyTransform2D" : "ApplyTransform", stream, channelName, Security.SecuritySendFlags.None); } } } }