/// <summary> /// Sends the named message /// </summary> /// <param name="name">The message name to send</param> /// <param name="clientIds">The clients to send to, sends to everyone if null</param> /// <param name="stream">The message stream containing the data</param> /// <param name="channel">The channel to send the data on</param> /// <param name="security">The security settings to apply to the message</param> public static void SendNamedMessage(string name, List <ulong> clientIds, Stream stream, string channel = null, SecuritySendFlags security = SecuritySendFlags.None) { ulong hash = NetworkedBehaviour.HashMethodName(name); using (PooledBitStream messageStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(messageStream)) { writer.WriteUInt64Packed(hash); } messageStream.CopyFrom(stream); if (!NetworkingManager.Singleton.IsServer) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { NetworkLog.LogWarning("Can not send named messages to multiple users as a client"); } return; } InternalMessageSender.Send(MLAPIConstants.MLAPI_NAMED_MESSAGE, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, clientIds, messageStream, security, null); } }
public void InvokeServerRPC(RPCDelegate method, Stream messageStream, byte channel = NetworkTransport.DEFAULT_CHANNEL) { if (!isClient) { //We are only a server and not a client Debug.LogError("Tried to invoke a ServerRPC without being a client. Only a client can invoke a server RPC.", this); return; } ulong hash = HashMethod(method.Method); using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteUInt64Packed(networkID); writer.WriteUInt64Packed(hash); stream.CopyFrom(messageStream); if (isHost) { messageStream.Position = 0; //Invoke local InvokeLocalServerRPC(hash, networkManager.clientID, stream); } else { MessageSender.Send(networkManager.serverID, m_NetworkBehaviourManager.serverRPCMessageType, channel, stream); } } } }
internal void SendClientRPCPerformance(ulong hash, uint clientId, Stream messageStream, string channel, SecuritySendFlags security) { if (!IsServer && IsRunning) { //We are NOT a server. if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Only clients and host can invoke ClientRPC"); } return; } using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteUInt32Packed(NetworkId); writer.WriteUInt16Packed(NetworkedObject.GetOrderIndex(this)); writer.WriteUInt64Packed(hash); stream.CopyFrom(messageStream); if (IsHost && clientId == NetworkingManager.Singleton.LocalClientId) { messageStream.Position = 0; InvokeClientRPCLocal(hash, NetworkingManager.Singleton.LocalClientId, messageStream); } else { InternalMessageHandler.Send(clientId, MLAPIConstants.MLAPI_CLIENT_RPC, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, stream, security); } } } }
internal static void Send(uint clientId, byte messageType, string channelName, Stream messageStream, bool skipQueue = false) { if (NetworkingManager.singleton.isServer && clientId == NetworkingManager.singleton.ServerClientId) { return; } using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteByte(messageType); stream.CopyFrom(messageStream); NetworkProfiler.StartEvent(TickType.Send, (uint)stream.Length, channelName, MLAPIConstants.MESSAGE_NAMES[messageType]); byte error; if (skipQueue) { netManager.NetworkConfig.NetworkTransport.QueueMessageForSending(clientId, stream.GetBuffer(), (int)stream.Length, MessageManager.channels[channelName], true, out error); } else { netManager.NetworkConfig.NetworkTransport.QueueMessageForSending(clientId, stream.GetBuffer(), (int)stream.Length, MessageManager.channels[channelName], false, out error); } NetworkProfiler.EndEvent(); } } }
internal static void Send(byte messageType, string channelName, uint clientIdToIgnore, Stream messageStream) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteByte(messageType); stream.CopyFrom(messageStream); NetworkProfiler.StartEvent(TickType.Send, (uint)stream.Length, channelName, MLAPIConstants.MESSAGE_NAMES[messageType]); for (int i = 0; i < netManager.ConnectedClientsList.Count; i++) { if (netManager.ConnectedClientsList[i].ClientId == clientIdToIgnore || (NetworkingManager.singleton.isServer && netManager.ConnectedClientsList[i].ClientId == NetworkingManager.singleton.ServerClientId)) { continue; } byte error; netManager.NetworkConfig.NetworkTransport.QueueMessageForSending(netManager.ConnectedClientsList[i].ClientId, stream.GetBuffer(), (int)stream.Length, MessageManager.channels[channelName], false, out error); } NetworkProfiler.EndEvent(); } } }
internal static void SendSpawnCallForObject(ulong clientId, NetworkedObject netObject, Stream payload) { using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteBool(netObject.IsPlayerObject); writer.WriteUInt64Packed(netObject.NetworkId); writer.WriteUInt64Packed(netObject.OwnerClientId); if (NetworkingManager.Singleton.NetworkConfig.UsePrefabSync) { writer.WriteUInt64Packed(netObject.PrefabHash); } else { writer.WriteBool(netObject.IsSceneObject == null ? true : netObject.IsSceneObject.Value); if (netObject.IsSceneObject == null || netObject.IsSceneObject.Value) { writer.WriteUInt64Packed(netObject.NetworkedInstanceId); } else { writer.WriteUInt64Packed(netObject.PrefabHash); } } writer.WriteBool(netObject.DestroyWithScene); 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); } if (NetworkingManager.Singleton.NetworkConfig.EnableNetworkedVar) { netObject.WriteNetworkedVarData(stream, clientId); } if (payload != null) { stream.CopyFrom(payload); } } InternalMessageHandler.Send(clientId, MLAPIConstants.MLAPI_ADD_OBJECT, "MLAPI_INTERNAL", stream, SecuritySendFlags.None, null); } }
//Server only public void SpawnOnNetworkServer(NetworkBehaviour behaviour, ServerBehaviourConnectedDelegate connectedCallback, BehaviourDisconnectedDelegate disconnectCallback, NetworkBehaviourRPCDelegate localRPCCallback, OwnerChangeDelegate ownerChangeCallback, ulong owner, List <ulong> observers, Stream spawnPayload) { if (behaviour == null || !behaviour.isNetworkSpawned || behaviour.isNetworkReady) { throw new InvalidOperationException("NetworkBehaviourManager.SpawnOnNetworkServer is only allowed to be called internally by a Network Behaviour."); } ulong newNetworkID = GetNewNetworkID(); if (!string.IsNullOrWhiteSpace(behaviour.uniqueID)) { ulong uniqueHash = behaviour.uniqueID.GetStableHash(networkManager.config.rpcHashSize); if (m_BehaviourByHashedID.TryGetValue(uniqueHash, out NetworkBehaviour otherBehaviour)) { if (!BehaviourWasDestroyed(otherBehaviour)) { throw new NetworkException("A Network Behaviour already has the unique ID '" + behaviour.uniqueID + "'."); } } if (m_HashedStrings.TryGetValue(uniqueHash, out string otherUniqueID)) { if (otherUniqueID == behaviour.uniqueID) { throw new NetworkException("A Network Behaviour already has the unique ID '" + behaviour.uniqueID + "'."); } else { //This occurs when 2 different strings hash to the same value from MLAPI.Hashing.GetStableHash. throw new NetworkException("A hash collision occurred. Either change the unique ID or increase the hash size in the config. '" + behaviour.uniqueID + "' and '" + otherUniqueID + "' both hashed to '" + uniqueHash + "'."); } } m_HashedStrings.Add(uniqueHash, behaviour.uniqueID); m_BehaviourByHashedID.Add(uniqueHash, behaviour); } m_NetworkBehaviours.Add(new NetworkBehaviourReference() { networkBehaviour = behaviour, connectedServerCallback = connectedCallback, disconnectedDelegate = disconnectCallback, localRPCDelegate = localRPCCallback, ownerChangeDelegate = ownerChangeCallback }); m_NetworkBehaviourDictionary.Add(newNetworkID, m_NetworkBehaviours[m_NetworkBehaviours.Count - 1]); //Spawn payload if (spawnPayload == null) { connectedCallback.Invoke(newNetworkID, owner, networkManager.serverID, observers, null); } else { using (PooledBitStream payloadStream = PooledBitStream.Get()) { payloadStream.CopyFrom(spawnPayload); payloadStream.Position = 0; connectedCallback.Invoke(newNetworkID, owner, networkManager.serverID, observers, payloadStream); } } }
/// <summary> /// Sends a named message /// </summary> /// <param name="name">The message name to send</param> /// <param name="clientId">The client to send the message to</param> /// <param name="stream">The message stream containing the data</param> /// <param name="channel">The channel tos end the data on</param> /// <param name="security">The security settings to apply to the message</param> public static void SendNamedMessage(string name, ulong clientId, Stream stream, string channel = null, SecuritySendFlags security = SecuritySendFlags.None) { ulong hash = NetworkedBehaviour.HashMethodName(name); using (PooledBitStream messageStream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(messageStream)) { writer.WriteUInt64Packed(hash); } messageStream.CopyFrom(stream); InternalMessageSender.Send(clientId, MLAPIConstants.MLAPI_NAMED_MESSAGE, string.IsNullOrEmpty(channel) ? "MLAPI_DEFAULT_MESSAGE" : channel, messageStream, security, null); } }
private void DoVisibleShowWrite(PooledBitStream stream, Stream spawnPayload) { if (!isServer) { throw new NotServerException("Only the server can change visibility of a Network Behaviour."); } if (!isNetworkSpawned) { throw new NetworkException("This Network Behaviour is not spawned on the network. Make sure this Network Behaviour is spawned using the NetworkBehaviour.SpawnOnNetwork function before changing it's visiblity."); } //Do message using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { //Write behaviour info and type writer.WriteUInt64Packed(networkID); writer.WriteUInt64Packed(ownerID); writer.WriteUInt64Packed(RPCTypeDefinition.GetHashFromType(GetType())); if (string.IsNullOrWhiteSpace(uniqueID)) { writer.WriteBool(false); } else { writer.WriteBool(true); writer.WriteUInt64Packed(m_UniqueHash); } writer.WriteBool(ownerCanUnspawn); writer.WriteBool(destroyOnUnspawn); //Write payload writer.WriteBool(spawnPayload != null); if (spawnPayload != null) { spawnPayload.Position = 0; writer.WriteInt32Packed((int)spawnPayload.Length); stream.CopyFrom(spawnPayload); } if (networkManager.enableLogging) { Debug.Log("Sending to clients the new behaviour " + GetType()); } } }
public void InvokeClientRPC(RPCDelegate method, ulong clientID, Stream messageStream, byte channel = NetworkTransport.DEFAULT_CHANNEL) { if (!isServer) { //We are only a client Debug.LogError("Tried to invoke a ClientRPC without being the server. Only the server can invoke a client RPC.", this); return; } ulong hash = HashMethod(method.Method); using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteUInt64Packed(networkID); writer.WriteUInt64Packed(hash); stream.CopyFrom(messageStream); if (isHost && clientID == networkManager.clientID) { messageStream.Position = 0; //Invoke local InvokeLocalClientRPC(hash, networkManager.clientID, messageStream); } else { if (!IsNetworkVisibleTo(clientID)) { if (IsClientPendingSpawn(clientID)) { Debug.LogError("The target client ID '" + clientID + "' is still a pending observer and cannot invoke remote RPCs on it until this Network Behaviour is network visible to that client.", this); } else { Debug.LogError("The target client ID '" + clientID + "' is not an observer of this Network Behaviour.", this); } return; } MessageSender.Send(clientID, m_NetworkBehaviourManager.clientRPCMessageType, channel, stream); } } } }
/// <summary> /// Encrypts a message with AES with a given key and a random salt that gets encoded as the first 16 bytes of the encrypted buffer /// </summary> /// <param name="clearStream">The stream to be encrypted</param> /// <param name="clientId">The clientId whose AES key to use</param> /// <returns>The encrypted stream with encoded salt</returns> public static Stream EncryptStream(Stream clearStream, uint clientId) { using (RijndaelManaged aes = new RijndaelManaged()) { aes.Key = NetworkingManager.singleton.isServer ? NetworkingManager.singleton.ConnectedClients[clientId].AesKey : NetworkingManager.singleton.clientAesKey; aes.GenerateIV(); using (CryptoStream cs = new CryptoStream(clearStream, aes.CreateEncryptor(), CryptoStreamMode.Read)) { using (PooledBitStream outStream = PooledBitStream.Get()) { outStream.Write(aes.IV, 0, 16); outStream.CopyFrom(cs); return(outStream); } } } }
/// <summary> /// Decrypts a message with AES with a given key and a salt that is encoded as the first 16 bytes of the buffer /// </summary> /// <param name="encryptedStream">The encrypted stream</param> /// <param name="clientId">The clientId whose AES key to use</param> /// <returns>The decrypted stream</returns> public static Stream DecryptStream(Stream encryptedStream, uint clientId) { encryptedStream.Read(IVBuffer, 0, 16); using (RijndaelManaged aes = new RijndaelManaged()) { aes.IV = IVBuffer; aes.Key = NetworkingManager.singleton.isServer ? NetworkingManager.singleton.ConnectedClients[clientId].AesKey : NetworkingManager.singleton.clientAesKey; using (CryptoStream cs = new CryptoStream(encryptedStream, aes.CreateDecryptor(), CryptoStreamMode.Read)) { using (PooledBitStream outStream = PooledBitStream.Get()) { outStream.CopyFrom(cs); return(outStream); } } } }
public void InvokeClientRPCAllExcept(RPCDelegate method, ulong clientIDToIgnore, Stream messageStream, byte channel = NetworkTransport.DEFAULT_CHANNEL) { if (!isServer) { //We are only a client Debug.LogError("Tried to invoke a ClientRPC without being the server. Only the server can invoke a client RPC.", this); return; } ulong hash = HashMethod(method.Method); using (PooledBitStream stream = PooledBitStream.Get()) { using (PooledBitWriter writer = PooledBitWriter.Get(stream)) { writer.WriteUInt64Packed(networkID); writer.WriteUInt64Packed(hash); stream.CopyFrom(messageStream); using (HashSet <ulong> .Enumerator observers = GetObservers()) { while (observers.MoveNext()) { if (observers.Current == clientIDToIgnore) { continue; } if (observers.Current == networkManager.clientID && isHost) { //Invoke local messageStream.Position = 0; InvokeLocalClientRPC(hash, networkManager.clientID, messageStream); } //Send to remote observer MessageSender.Send(observers.Current, m_NetworkBehaviourManager.clientRPCMessageType, channel, stream); } } } } }
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); } } } }
//Server only internal static void SpawnPlayerObject(NetworkedObject netObject, uint clientId, 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; } else if (netManager.ConnectedClients[clientId].PlayerObject != null) { if (LogHelper.CurrentLogLevel <= LogLevel.Normal) { LogHelper.LogWarning("Client already have a player object"); } return; } uint netId = GetNetworkObjectId(); netObject.NetworkId = netId; SpawnedObjects.Add(netId, netObject); SpawnedObjectsList.Add(netObject); netObject.isSpawned = true; netObject.sceneObject = false; netObject.isPlayerObject = true; netManager.ConnectedClients[clientId].PlayerObject = 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(true); 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); } } } }