internal static void OnUpdateVarsMessage(UpdateVarsMessage msg) { if (LogFilter.Debug) { Debug.Log("ClientScene.OnUpdateVarsMessage " + msg.netId); } if (NetworkIdentity.spawned.TryGetValue(msg.netId, out NetworkIdentity localObject) && localObject != null) { using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(msg.payload)) localObject.OnDeserializeAllSafely(networkReader, false); } else { Debug.LogWarning("Did not find target for sync message for " + msg.netId + " . Note: this can be completely normal because UDP messages may arrive out of order, so this message might have arrived after a Destroy message."); } }
internal void OnSyncEventMessage(SyncEventMessage msg) { if (logger.LogEnabled()) { logger.Log("ClientScene.OnSyncEventMessage " + msg.netId); } if (Spawned.TryGetValue(msg.netId, out NetworkIdentity identity)) { using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(msg.payload)) identity.HandleSyncEvent(msg.componentIndex, msg.functionHash, networkReader); } else { logger.LogWarning("Did not find target for SyncEvent message for " + msg.netId); } }
static void ApplySpawnPayload(NetworkIdentity identity, SpawnMessage msg) { identity.Reset(); if (msg.assetId != Guid.Empty) { identity.assetId = msg.assetId; } if (!identity.gameObject.activeSelf) { identity.gameObject.SetActive(true); } // apply local values for VR support identity.transform.localPosition = msg.position; identity.transform.localRotation = msg.rotation; identity.transform.localScale = msg.scale; identity.hasAuthority = msg.isOwner; identity.netId = msg.netId; if (msg.isLocalPlayer) { InternalAddPlayer(identity); } // deserialize components if any payload // (Count is 0 if there were no components) if (msg.payload.Count > 0) { using (PooledNetworkReader payloadReader = NetworkReaderPool.GetReader(msg.payload)) { identity.OnDeserializeAllSafely(payloadReader, true); } } NetworkIdentity.spawned[msg.netId] = identity; // objects spawned as part of initial state are started on a second pass if (isSpawnFinished) { identity.NotifyAuthority(); identity.OnStartClient(); CheckForLocalPlayer(identity); } }
/// <summary> /// This function invokes the registered handler function for a message. /// <para>Network connections used by the NetworkClient and NetworkServer use this function for handling network messages.</para> /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message object to process.</param> /// <returns>Returns true if the handler was successfully invoked</returns> public bool InvokeHandler <T>(T msg, int channelId) where T : struct, NetworkMessage { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // if it is a value type, just use typeof(T) to avoid boxing // this works because value types cannot be derived // if it is a reference type (for example NetworkMessage), // ask the message for the real type int msgType = MessagePacker.GetId <T>(); MessagePacker.Pack(msg, writer); ArraySegment <byte> segment = writer.ToArraySegment(); using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(segment)) return(InvokeHandler(msgType, networkReader, channelId)); } }
void CmdOnAnimationServerMessage(int stateHash, float normalizedTime, int layerId, float weight, byte[] parameters) { // Ignore messages from client if not in client authority mode if (!clientAuthority) { return; } // Debug.Log("OnAnimationMessage for netId=" + netId); // handle and broadcast using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(parameters)) { HandleAnimMsg(stateHash, normalizedTime, layerId, weight, networkReader); RpcOnAnimationClientMessage(stateHash, normalizedTime, layerId, weight, parameters); } }
// unpack a message we received public static T Unpack <T>(byte[] data) where T : IMessageBase, new() { using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(data)) { int msgType = GetId <T>(); int id = networkReader.ReadUInt16(); if (id != msgType) { throw new FormatException("Invalid message, could not unpack " + typeof(T).FullName); } T message = new T(); message.Deserialize(networkReader); return(message); } }
internal static void OnSyncEventMessage(SyncEventMessage msg) { if (LogFilter.Debug) { Debug.Log("ClientScene.OnSyncEventMessage " + msg.netId); } if (NetworkIdentity.spawned.TryGetValue(msg.netId, out var identity)) { var networkReader = NetworkReaderPool.GetReader(msg.payload); identity.HandleSyncEvent(msg.componentIndex, msg.functionHash, networkReader); NetworkReaderPool.Recycle(networkReader); } else { Debug.LogWarning("Did not find target for SyncEvent message for " + msg.netId); } }
void CmdClientToServerSync(byte[] payload) { // deserialize payload NetworkReader networkReader = NetworkReaderPool.GetReader(payload); DeserializeFromReader(networkReader); NetworkReaderPool.Recycle(networkReader); // server-only mode does no interpolation to save computations, // but let's set the position directly if (isServer && !isClient) { ApplyPositionRotationScale(goal.localPosition, goal.localRotation, goal.localScale); } // set dirty so that OnSerialize broadcasts it SetDirtyBit(1UL); }
/// <summary> /// This function invokes the registered handler function for a message. /// <para>Network connections used by the NetworkClient and NetworkServer use this function for handling network messages.</para> /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message object to process.</param> /// <returns></returns> public bool InvokeHandler <T>(T msg, int channelId) where T : IMessageBase { // get writer from pool using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // if it is a value type, just use typeof(T) to avoid boxing // this works because value types cannot be derived // if it is a reference type (for example IMessageBase), // ask the message for the real type int msgType = MessagePacker.GetId(typeof(T).IsValueType ? typeof(T) : msg.GetType()); Debug.Log("[NetworkConnection].InvokeHandler -- msg: " + msg.ToString()); MessagePacker.Pack(msg, writer); ArraySegment <byte> segment = writer.ToArraySegment(); using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(segment)) return(InvokeHandler(msgType, networkReader, channelId)); } }
internal void OnRpcMessage(RpcMessage msg) { if (logger.LogEnabled()) { logger.Log("ClientScene.OnRPCMessage hash:" + msg.functionHash + " netId:" + msg.netId); } Skeleton skeleton = RemoteCallHelper.GetSkeleton(msg.functionHash); if (skeleton.invokeType != MirrorInvokeType.ClientRpc) { throw new MethodInvocationException($"Invalid RPC call with id {msg.functionHash}"); } if (client.Spawned.TryGetValue(msg.netId, out NetworkIdentity identity) && identity != null) { using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(msg.payload)) identity.HandleRemoteCall(skeleton, msg.componentIndex, networkReader); } }
// note: original HLAPI HandleBytes function handled >1 message in a while loop, but this wasn't necessary // anymore because NetworkServer/NetworkClient Update both use while loops to handle >1 data events per // frame already. // -> in other words, we always receive 1 message per Receive call, never two. // -> can be tested easily with a 1000ms send delay and then logging amount received in while loops here // and in NetworkServer/Client Update. HandleBytes already takes exactly one. /// <summary> /// This function allows custom network connection classes to process data from the network before it is passed to the application. /// </summary> /// <param name="buffer">The data received.</param> internal void TransportReceive(ArraySegment <byte> buffer, int channelId) { // unpack message using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(buffer)) { try { int msgType = MessagePacker.UnpackId(networkReader); // try to invoke the handler for that message InvokeHandler(msgType, networkReader, channelId); } catch (Exception ex) { Debug.LogError("Closed connection: " + this + ". Invalid message " + ex); Disconnect(); } } }
/// <summary> /// This function invokes the registered handler function for a message. /// <para>Network connections used by the NetworkClient and NetworkServer use this function for handling network messages.</para> /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message object to process.</param> /// <returns></returns> public bool InvokeHandler <T>(T msg, int channelId) where T : IMessageBase { // get writer from pool NetworkWriter writer = NetworkWriterPool.GetWriter(); // pack and invoke int msgType = MessagePacker.GetId(typeof(T).IsValueType ? typeof(T) : msg.GetType()); MessagePacker.Pack(msg, writer); ArraySegment <byte> segment = writer.ToArraySegment(); NetworkReader reader = NetworkReaderPool.GetReader(segment); bool result = InvokeHandler(msgType, reader, channelId); NetworkReaderPool.Recycle(reader); // recycle writer and return NetworkWriterPool.Recycle(writer); return(result); }
// unpack a message we received public static T Unpack <T>(byte[] data) where T : IMessageBase, new() { NetworkReader reader = NetworkReaderPool.GetReader(data); int msgType = GetId <T>(); int id = reader.ReadUInt16(); if (id != msgType) { throw new FormatException("Invalid message, could not unpack " + typeof(T).FullName); } T message = typeof(T).IsValueType ? default : new T(); message.Deserialize(reader); NetworkReaderPool.Recycle(reader); return(message); }
void CmdOnMotionServerMessage(int stateHash, byte[] parameters) { // Ignore messages from client if not in client authority mode if (!clientAuthority) { return; } if (logger.LogEnabled()) { logger.Log("OnAnimationMessage for netId=" + netId); } // handle and broadcast using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(parameters)) { HandleMotionMsg(stateHash, networkReader); RpcOnMotionClientMessage(stateHash, parameters); } }
public void ReadBytesCountTooBigTest() { // calling ReadBytes with a count bigger than what is in Reader // should throw an exception byte[] bytes = { 0x00, 0x01 }; using (PooledNetworkReader reader = NetworkReaderPool.GetReader(bytes)) { try { reader.ReadBytes(bytes, bytes.Length + 1); // BAD: IF WE GOT HERE, THEN NO EXCEPTION WAS THROWN Assert.Fail(); } catch (EndOfStreamException) { // GOOD } } }
public void ShrinkCapacity() { NetworkReaderPool.Capacity = 2; // get Reader and recycle so we have 2 in there, hence 'next' is at limit PooledNetworkReader a = NetworkReaderPool.GetReader(default(ArraySegment <byte>)); PooledNetworkReader b = NetworkReaderPool.GetReader(default(ArraySegment <byte>)); NetworkReaderPool.Recycle(a); NetworkReaderPool.Recycle(b); // shrink NetworkReaderPool.Capacity = 1; // get one. should return the only one which is still in there. PooledNetworkReader c = NetworkReaderPool.GetReader(default(ArraySegment <byte>)); Assert.That(c, !Is.Null); Assert.That(c == a || c == b); }
void CmdClientToServerSync(byte[] payload) { // Ignore messages from client if not in client authority mode if (!clientAuthority) { return; } // deserialize payload using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(payload)) DeserializeFromReader(networkReader); // server-only mode does no interpolation to save computations, // but let's set the position directly if (isServer && !isClient) { ApplyPositionRotationScale(goal.thePosition, goal.theRotation, goal.theScale); } // set dirty so that OnSerialize broadcasts it SetDirtyBit(1UL); }
/// <summary> /// This function invokes the registered handler function for a message. /// <para>Network connections used by the NetworkClient and NetworkServer use this function for handling network messages.</para> /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message object to process.</param> /// <returns></returns> public bool InvokeHandler <T>(T msg, int channelId) where T : IMessageBase { // get writer from pool NetworkWriter writer = NetworkWriterPool.GetWriter(); // if it is a value type, just use typeof(T) to avoid boxing // this works because value types cannot be derived // if it is a reference type (for example IMessageBase), // ask the message for the real type int msgType = MessagePacker.GetId(typeof(T).IsValueType ? typeof(T) : msg.GetType()); MessagePacker.Pack(msg, writer); ArraySegment <byte> segment = writer.ToArraySegment(); NetworkReader networkReader = NetworkReaderPool.GetReader(segment); bool result = InvokeHandler(msgType, networkReader, channelId); NetworkReaderPool.Recycle(networkReader); // recycle writer and return NetworkWriterPool.Recycle(writer); return(result); }
/// <summary> /// This function allows custom network connection classes to process data from the network before it is passed to the application. /// </summary> /// <param name="buffer">The data received.</param> /// <param name="channelId"></param> internal void TransportReceive(ArraySegment <byte> buffer, int channelId) { if (buffer.Count < MessagePacker.HeaderSize) { logger.LogError($"ConnectionRecv {this} Message was too short (messages should start with message id)"); Disconnect(); return; } // unpack message using (PooledNetworkReader reader = NetworkReaderPool.GetReader(buffer)) { // the other end might batch multiple messages into one packet. // we need to try to unpack multiple times. while (reader.Position < reader.Length) { if (!UnpackAndInvoke(reader, channelId)) { break; } } } }
public void PoolReUsesReadersUpToSizeLimit() { NetworkReaderPool.Capacity = 1; // get 2 Readers PooledNetworkReader a = NetworkReaderPool.GetReader(default(ArraySegment <byte>)); PooledNetworkReader b = NetworkReaderPool.GetReader(default(ArraySegment <byte>)); // recycle all NetworkReaderPool.Recycle(a); NetworkReaderPool.Recycle(b); // get 2 new ones PooledNetworkReader c = NetworkReaderPool.GetReader(default(ArraySegment <byte>)); PooledNetworkReader d = NetworkReaderPool.GetReader(default(ArraySegment <byte>)); // exactly one should be reused, one should be new bool cReused = c == a || c == b; bool dReused = d == a || d == b; Assert.That((cReused && !dReused) || (!cReused && dReused)); }
/// <summary> /// Handle ServerRpc from specific player, this could be one of multiple players on a single client /// </summary> /// <param name="conn"></param> /// <param name="msg"></param> void OnServerRpcMessage(INetworkConnection conn, ServerRpcMessage msg) { if (!server.Spawned.TryGetValue(msg.netId, out NetworkIdentity identity) || identity is null) { if (logger.WarnEnabled()) { logger.LogWarning("Spawned object not found when handling ServerRpc message [netId=" + msg.netId + "]"); } return; } Skeleton skeleton = RemoteCallHelper.GetSkeleton(msg.functionHash); if (skeleton.invokeType != MirrorInvokeType.ServerRpc) { throw new MethodInvocationException($"Invalid ServerRpc for id {msg.functionHash}"); } // ServerRpcs can be for player objects, OR other objects with client-authority // -> so if this connection's controller has a different netId then // only allow the ServerRpc if clientAuthorityOwner if (skeleton.cmdRequireAuthority && identity.ConnectionToClient != conn) { if (logger.WarnEnabled()) { logger.LogWarning("ServerRpc for object without authority [netId=" + msg.netId + "]"); } return; } if (logger.LogEnabled()) { logger.Log("OnServerRpcMessage for netId=" + msg.netId + " conn=" + conn); } using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(msg.payload)) identity.HandleRemoteCall(skeleton, msg.componentIndex, networkReader, conn, msg.replyId); }
void RpcOnAnimationParametersClientMessage(byte[] parameters) { using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(parameters)) HandleAnimParamsMsg(networkReader); }
void RpcOnAnimationClientMessage(int stateHash, float normalizedTime, int layerId, byte[] parameters) { using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(parameters)) HandleAnimMsg(stateHash, normalizedTime, layerId, networkReader); }
public void Dispose() { NetworkReaderPool.Recycle(this); }
public void Dispose() => NetworkReaderPool.Return(this);