/// <summary> /// This sends a network message with a message ID on the connection. This message is sent on channel zero, which by default is the reliable channel. /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message to send.</param> /// <param name="channelId">The transport layer channel to send on.</param> /// <returns></returns> public bool Send <T>(T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message and send allocation free MessagePacker.Pack(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Position, 1); return(Send(writer.ToArraySegment(), channelId)); } }
public static byte[] Pack <T>(T message) where T : IMessageBase { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { Pack(message, writer); byte[] data = writer.ToArray(); return(data); } }
/// <summary> /// This sends a network message to the connection. You can await it to check for errors /// </summary> /// <typeparam name="T">The message type</typeparam> /// <param name="msg">The message to send.</param> /// <param name="channelId">The transport layer channel to send on.</param> /// <returns></returns> public virtual UniTask SendAsync <T>(T msg, int channelId = Channel.Reliable) { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message and send allocation free MessagePacker.Pack(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Length, 1); return(SendAsync(writer.ToArraySegment(), channelId)); } }
// send a batch. internal so we can test it. internal void SendBatch(int channelId, Batch batch) { // get max batch size for this channel int max = Transport.activeTransport.GetMaxBatchSize(channelId); // we need a writer to merge queued messages into a batch using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // for each queued message while (batch.messages.Count > 0) { // get it PooledNetworkWriter message = batch.messages.Dequeue(); ArraySegment <byte> segment = message.ToArraySegment(); // IF adding to writer would end up >= MTU then we should // flush first. the goal is to always flush < MTU packets. // // IMPORTANT: if writer is empty and segment is > MTU // (which can happen for large max sized message) // then we would send an empty previous writer. // => don't do that. // => only send if not empty. if (writer.Position > 0 && writer.Position + segment.Count >= max) { // flush & reset writer Transport.activeTransport.ServerSend(connectionId, writer.ToArraySegment(), channelId); writer.Position = 0; } // now add to writer in any case // -> WriteBytes instead of WriteSegment because the latter // would add a size header. we want to write directly. // // NOTE: it's very possible that we add > MTU to writer if // message size is > MTU. // which is fine. next iteration will just flush it. writer.WriteBytes(segment.Array, segment.Offset, segment.Count); // return queued message to pool NetworkWriterPool.Recycle(message); } // done iterating queued messages. // batch might still contain the last message. // send it. if (writer.Position > 0) { Transport.activeTransport.ServerSend(connectionId, writer.ToArraySegment(), channelId); writer.Position = 0; } } }
internal static byte[] PackWithAlloc <T>(T message) where T : IMessageBase { NetworkWriter writer = NetworkWriterPool.GetWriter(); Pack(message, writer); byte[] data = writer.ToArray(); NetworkWriterPool.Recycle(writer); return(data); }
/// <summary>Send a NetworkMessage to this connection over the given channel.</summary> public void Send <T>(T msg, int channelId = Channels.Reliable) where T : struct, NetworkMessage { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message and send allocation free MessagePacking.Pack(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Position, 1); Send(writer.ToArraySegment(), channelId); } }
public static byte[] Pack <T>(T message) where T : IMessageBase { var writer = NetworkWriterPool.GetWriter(); Pack(message, writer); var data = writer.ToArray(); NetworkWriterPool.Recycle(writer); return(data); }
// add a message for batching // we allow any sized messages. // caller needs to make sure they are within max packet size. public void AddMessage(ArraySegment <byte> message) { // put into a (pooled) writer // -> WriteBytes instead of WriteSegment because the latter // would add a size header. we want to write directly. // -> will be returned to pool when making the batch! // IMPORTANT: NOT adding a size header / msg saves LOTS of bandwidth PooledNetworkWriter writer = NetworkWriterPool.GetWriter(); writer.WriteBytes(message.Array, message.Offset, message.Count); messages.Enqueue(writer); }
/// <summary> /// This sends a network message with a message ID on the connection. This message is sent on channel zero, which by default is the reliable channel. /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message to send.</param> /// <param name="channelId">The transport layer channel to send on.</param> /// <returns></returns> public virtual bool Send <T>(T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { NetworkWriter writer = NetworkWriterPool.GetWriter(); // pack message and send allocation free MessagePacker.Pack(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Position, 1); bool result = Send(writer.ToArraySegment(), channelId); NetworkWriterPool.Recycle(writer); return(result); }
// Send stage two: serialized NetworkMessage as ArraySegment<byte> internal override void Send(ArraySegment <byte> segment, int channelId = Channels.Reliable) { // get a writer to copy the message into since the segment is only // valid until returning. // => pooled writer will be returned to pool when dequeuing. // => WriteBytes instead of WriteArraySegment because the latter // includes a 4 bytes header. we just want to write raw. //Debug.Log($"Enqueue {BitConverter.ToString(segment.Array, segment.Offset, segment.Count)}"); PooledNetworkWriter writer = NetworkWriterPool.GetWriter(); writer.WriteBytes(segment.Array, segment.Offset, segment.Count); connectionToServer.queue.Enqueue(writer); }
void CheckSendRate() { if (sendMessagesAllowed && syncInterval > 0 && sendTimer < Time.time) { sendTimer = Time.time + syncInterval; NetworkWriter writer = NetworkWriterPool.GetWriter(); if (WriteParameters(writer)) { SendAnimationParametersMessage(writer.ToArray()); } NetworkWriterPool.Recycle(writer); } }
/// <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) where T : IMessageBase { // get writer from pool NetworkWriter writer = NetworkWriterPool.GetWriter(); // pack and invoke int msgType = MessagePacker.GetId <T>(); MessagePacker.Pack(msg, writer); bool result = InvokeHandler(msgType, new NetworkReader(writer.ToArraySegment())); // recycle writer and return NetworkWriterPool.Recycle(writer); return(result); }
void CheckSendRate() { if (SendMessagesAllowed && syncInterval > 0 && sendTimer < Time.time) { sendTimer = Time.time + syncInterval; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { if (WriteParameters(writer)) { SendMotionParametersMessage(writer.ToArray()); } } } }
/// <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 : 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(default(T) != null ? typeof(T) : msg.GetType()); MessagePacker.Pack(msg, writer); ArraySegment <byte> segment = writer.ToArraySegment(); using (PooledNetworkReader networkReader = NetworkReaderPool.GetReader(segment)) return(InvokeHandler(msgType, networkReader, channelId)); } }
void CheckSendRate() { float now = Time.time; if (SendMessagesAllowed && syncInterval >= 0 && now > nextSendTime) { nextSendTime = now + syncInterval; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { if (WriteParameters(writer)) { SendAnimationParametersMessage(writer.ToArray()); } } } }
void UpdateClient() { // send to server if we have local authority (and aren't the server) // -> only if connectionToServer has been initialized yet too // check only each 'syncInterval' if (!IsServer && IsClientWithAuthority && Time.time - lastClientSendTime >= syncInterval) { if (HasEitherMovedRotatedScaled()) { // serialize // local position/rotation for VR support using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { SerializeIntoWriter(writer, TargetComponent.transform.localPosition, TargetComponent.transform.localRotation, TargetComponent.transform.localScale); // send to server CmdClientToServerSync(writer.ToArray()); } } lastClientSendTime = Time.time; } // apply interpolation on client for all players // unless this client has authority over the object. could be // himself or another object that he was assigned authority over if (!IsClientWithAuthority && goal != null) { // teleport or interpolate if (NeedsTeleport()) { // local position/rotation for VR support ApplyPositionRotationScale(goal.LocalPosition, goal.LocalRotation, goal.LocalScale); // reset data points so we don't keep interpolating start = null; goal = null; } else { // local position/rotation for VR support ApplyPositionRotationScale(InterpolatePosition(start, goal, TargetComponent.transform.localPosition), InterpolateRotation(start, goal, TargetComponent.transform.localRotation), InterpolateScale(start, goal, TargetComponent.transform.localScale)); } } }
internal void SendSpawnMessage(NetworkIdentity identity, NetworkConnection conn) { if (identity.serverOnly) { return; } // for easier debugging if (LogFilter.Debug) { Debug.Log("Server SendSpawnMessage: name=" + identity.name + " sceneId=" + identity.sceneId.ToString("X") + " netid=" + identity.NetId); } // one writer for owner, one for observers using (PooledNetworkWriter ownerWriter = NetworkWriterPool.GetWriter(), observersWriter = NetworkWriterPool.GetWriter()) { // serialize all components with initialState = true // (can be null if has none) (int ownerWritten, int observersWritten) = identity.OnSerializeAllSafely(true, ownerWriter, observersWriter); // convert to ArraySegment to avoid reader allocations // (need to handle null case too) ArraySegment <byte> ownerSegment = ownerWritten > 0 ? ownerWriter.ToArraySegment() : default; ArraySegment <byte> observersSegment = observersWritten > 0 ? observersWriter.ToArraySegment() : default; var msg = new SpawnMessage { netId = identity.NetId, isLocalPlayer = conn.Identity == identity, isOwner = identity.ConnectionToClient == conn, sceneId = identity.sceneId, assetId = identity.AssetId, // use local values for VR support position = identity.transform.localPosition, rotation = identity.transform.localRotation, scale = identity.transform.localScale }; // use owner segment if 'conn' owns this identity, otherwise // use observers segment msg.payload = msg.isOwner ? ownerSegment : observersSegment; conn.Send(msg); } }
public static byte[] PackMessage(int msgType, MessageBase msg) { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { try { // write message type writer.WriteInt16((short)msgType); // serialize message into writer msg.Serialize(writer); // return byte[] return(writer.ToArray()); } finally { } } }
/// <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)); } }
void FixedUpdate() { if (!SendMessagesAllowed) { return; } CheckSendRate(); if (m_MotionStateUpdate) { //Update the motionHash using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { WriteParameters(writer); SendMotionMessage(m_Motion.currentState.serializationKey, writer.ToArray()); } } }
/// <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(); bool result = InvokeHandler(msgType, new NetworkReader(segment), channelId); // recycle writer and return NetworkWriterPool.Recycle(writer); return(result); }
/// <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); }
public static void Send <T>(IEnumerable <NetworkConnection> connections, T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message into byte[] once MessagePacker.Pack(msg, writer); var segment = writer.ToArraySegment(); int count = 0; foreach (NetworkConnection conn in connections) { // send to all connections, but don't wait for them _ = conn.SendAsync(segment); count++; } NetworkDiagnostics.OnSend(msg, channelId, segment.Count, count); } }
internal override void Update() { base.Update(); // should we still process a connected event? if (connectedEventPending) { connectedEventPending = false; NetworkClient.OnConnectedEvent?.Invoke(); } // process internal messages so they are applied at the correct time while (queue.Count > 0) { // call receive on queued writer's content, return to pool PooledNetworkWriter writer = queue.Dequeue(); ArraySegment <byte> message = writer.ToArraySegment(); // OnTransportData assumes a proper batch with timestamp etc. // let's make a proper batch and pass it to OnTransportData. Batcher batcher = GetBatchForChannelId(Channels.Reliable); batcher.AddMessage(message); using (PooledNetworkWriter batchWriter = NetworkWriterPool.GetWriter()) { // make a batch with our local time (double precision) if (batcher.MakeNextBatch(batchWriter, NetworkTime.localTime)) { NetworkClient.OnTransportData(batchWriter.ToArraySegment(), Channels.Reliable); } } NetworkWriterPool.Recycle(writer); } // should we still process a disconnected event? if (disconnectedEventPending) { disconnectedEventPending = false; NetworkClient.OnDisconnectedEvent?.Invoke(); } }
public static byte[] PackMessage(int msgType, MessageBase msg) { NetworkWriter writer = NetworkWriterPool.GetWriter(); try { // write message type writer.Write((short)msgType); // serialize message into writer msg.Serialize(writer); // return byte[] return(writer.ToArray()); } finally { NetworkWriterPool.Recycle(writer); } }
// pack message before sending public static byte[] Pack <T>(T message) where T : IMessageBase { NetworkWriter writer = NetworkWriterPool.GetWriter(); try { // write message type int msgType = GetId <T>(); writer.WriteUInt16((ushort)msgType); // serialize message into writer message.Serialize(writer); // return byte[] return(writer.ToArray()); } finally { NetworkWriterPool.Recycle(writer); } }
internal void SendSpawnMessage(NetworkIdentity identity, INetworkConnection conn) { if (identity.serverOnly) { return; } // for easier debugging if (logger.LogEnabled()) { logger.Log("Server SendSpawnMessage: name=" + identity.name + " sceneId=" + identity.sceneId.ToString("X") + " netid=" + identity.NetId); } // one writer for owner, one for observers using (PooledNetworkWriter ownerWriter = NetworkWriterPool.GetWriter(), observersWriter = NetworkWriterPool.GetWriter()) { bool isOwner = identity.ConnectionToClient == conn; ArraySegment <byte> payload = CreateSpawnMessagePayload(isOwner, identity, ownerWriter, observersWriter); var msg = new SpawnMessage { netId = identity.NetId, isLocalPlayer = conn.Identity == identity, isOwner = isOwner, sceneId = identity.sceneId, assetId = identity.AssetId, // use local values for VR support position = identity.transform.localPosition, rotation = identity.transform.localRotation, scale = identity.transform.localScale, payload = payload, }; conn.Send(msg); } }
void FixedUpdate() { if (!SendMessagesAllowed) { return; } CheckSendRate(); for (int i = 0; i < Animator.layerCount; i++) { if (!CheckAnimStateChanged(out int stateHash, out float normalizedTime, i)) { continue; } using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { WriteParameters(writer); SendAnimationMessage(stateHash, normalizedTime, i, writer.ToArray()); } } }
void Update() { // if server then always sync to others. if (isServer) { // just use OnSerialize via SetDirtyBit only sync when position // changed. set dirty bits 0 or 1 SetDirtyBit(HasEitherMovedRotatedScaled() ? 1UL : 0UL); } // no 'else if' since host mode would be both if (isClient) { // send to server if we have local authority (and aren't the server) // -> only if connectionToServer has been initialized yet too if (!isServer && isClientWithAuthority) { // check only each 'syncInterval' if (Time.time - lastClientSendTime >= syncInterval) { if (HasEitherMovedRotatedScaled()) { // serialize // local position/rotation for VR support NetworkWriter writer = NetworkWriterPool.GetWriter(); SerializeIntoWriter(writer, targetComponent.transform.localPosition, targetComponent.transform.localRotation, compressRotation, targetComponent.transform.localScale); // send to server CmdClientToServerSync(writer.ToArray()); NetworkWriterPool.Recycle(writer); } lastClientSendTime = Time.time; } } // apply interpolation on client for all players // unless this client has authority over the object. could be // himself or another object that he was assigned authority over if (!isClientWithAuthority) { // received one yet? (initialized?) if (goal != null) { // teleport or interpolate if (NeedsTeleport()) { // local position/rotation for VR support ApplyPositionRotationScale(goal.localPosition, goal.localRotation, goal.localScale); // reset data points so we don't keep interpolating start = null; goal = null; } else { // local position/rotation for VR support ApplyPositionRotationScale(InterpolatePosition(start, goal, targetComponent.transform.localPosition), InterpolateRotation(start, goal, targetComponent.transform.localRotation), InterpolateScale(start, goal, targetComponent.transform.localScale)); } } } } }
internal static void SendSpawnMessage(NetworkIdentity identity, NetworkConnection conn) { if (identity.serverOnly) { return; } if (LogFilter.Debug) { Debug.Log("Server SendSpawnMessage: name=" + identity.name + " sceneId=" + identity.sceneId.ToString("X") + " netid=" + identity.netId); // for easier debugging } NetworkWriter writer = NetworkWriterPool.GetWriter(); // convert to ArraySegment to avoid reader allocations // (need to handle null case too) ArraySegment <byte> segment = default; // serialize all components with initialState = true // (can be null if has none) if (identity.OnSerializeAllSafely(true, writer)) { segment = writer.ToArraySegment(); } // 'identity' is a prefab that should be spawned if (identity.sceneId == 0) { SpawnPrefabMessage msg = new SpawnPrefabMessage { netId = identity.netId, owner = conn?.playerController == identity, assetId = identity.assetId, // use local values for VR support position = identity.transform.localPosition, rotation = identity.transform.localRotation, scale = identity.transform.localScale, payload = segment }; // conn is != null when spawning it for a client if (conn != null) { conn.Send(msg); } // conn is == null when spawning it for the local player else { SendToReady(identity, msg); } } // 'identity' is a scene object that should be spawned again else { SpawnSceneObjectMessage msg = new SpawnSceneObjectMessage { netId = identity.netId, owner = conn?.playerController == identity, sceneId = identity.sceneId, // use local values for VR support position = identity.transform.localPosition, rotation = identity.transform.localRotation, scale = identity.transform.localScale, payload = segment }; // conn is != null when spawning it for a client if (conn != null) { conn.Send(msg); } // conn is == null when spawning it for the local player else { SendToReady(identity, msg); } } NetworkWriterPool.Recycle(writer); }