void CheckSendRate() { if (SendMessagesAllowed && syncInterval > 0 && sendTimer < Time.time) { sendTimer = Time.time + syncInterval; using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { if (WriteParameters(writer)) { SendAnimationParametersMessage(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 { } } }
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); } }
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 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 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(); } }
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); } }
// batch as many messages as possible into writer // returns true if any batch was made. public bool MakeNextBatch(NetworkWriter writer, double timeStamp) { // if we have no messages then there's nothing to do if (messages.Count == 0) { return(false); } // make sure the writer is fresh to avoid uncertain situations if (writer.Position != 0) { throw new ArgumentException($"MakeNextBatch needs a fresh writer!"); } // write timestamp first // -> double precision for accuracy over long periods of time writer.WriteDouble(timeStamp); // do start no matter what do { // add next message no matter what. even if > threshold. // (we do allow > threshold sized messages as single batch) PooledNetworkWriter message = messages.Dequeue(); ArraySegment <byte> segment = message.ToArraySegment(); writer.WriteBytes(segment.Array, segment.Offset, segment.Count); // return the writer to pool NetworkWriterPool.Recycle(message); } // keep going as long as we have more messages, // AND the next one would fit into threshold. while (messages.Count > 0 && writer.Position + messages.Peek().Position <= threshold); // we had messages, so a batch was made return(true); }
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()); } } }
// helper function to start reading a batch. void StartReadingBatch(PooledNetworkWriter batch) { // point reader to it reader.SetBuffer(batch.ToArraySegment()); }
// get next message, unpacked from batch (if any) // timestamp is the REMOTE time when the batch was created remotely. public bool GetNextMessage(out NetworkReader message, out double remoteTimeStamp) { // getting messages would be easy via // <<size, message, size, message, ...>> // but to save A LOT of bandwidth, we use // <<message, message, ...> // in other words, we don't know where the current message ends // // BUT: it doesn't matter! // -> we simply return the reader // * if we have one yet // * and if there's more to read // -> the caller can then read one message from it // -> when the end is reached, we retire the batch! // // for example: // while (GetNextMessage(out message)) // ProcessMessage(message); // message = null; // do nothing if we don't have any batches. // otherwise the below queue.Dequeue() would throw an // InvalidOperationException if operating on empty queue. if (batches.Count == 0) { remoteTimeStamp = 0; return(false); } // was our reader pointed to anything yet? if (reader.Length == 0) { remoteTimeStamp = 0; return(false); } // no more data to read? if (reader.Remaining == 0) { // retire the batch PooledNetworkWriter writer = batches.Dequeue(); NetworkWriterPool.Recycle(writer); // do we have another batch? if (batches.Count > 0) { // point reader to the next batch. // we'll return the reader below. PooledNetworkWriter next = batches.Peek(); StartReadingBatch(next); } // otherwise there's nothing more to read else { remoteTimeStamp = 0; return(false); } } // use the current batch's remote timestamp // AFTER potentially moving to the next batch ABOVE! remoteTimeStamp = readerRemoteTimeStamp; // if we got here, then we have more data to read. message = reader; return(true); }
public static void Recycle(PooledNetworkWriter writer) { pool.Push(writer); }
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 using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { if (useGlobalPosition) { SerializeIntoWriter(writer, targetComponent.transform.position, targetComponent.transform.rotation, targetComponent.transform.localScale); } else { 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) { // received one yet? (initialized?) if (goal != null) { // teleport or interpolate if (NeedsTeleport()) { // local position/rotation for VR support ApplyPositionRotationScale(goal.thePosition, goal.theRotation, goal.theScale); // reset data points so we don't keep interpolating start = null; goal = null; } else { // local position/rotation for VR support if (useGlobalPosition) { ApplyPositionRotationScale(InterpolatePosition(start, goal, targetComponent.transform.position), InterpolateRotation(start, goal, targetComponent.transform.rotation), InterpolateScale(start, goal, targetComponent.transform.localScale)); } else { ApplyPositionRotationScale(InterpolatePosition(start, goal, targetComponent.transform.localPosition), InterpolateRotation(start, goal, targetComponent.transform.localRotation), InterpolateScale(start, goal, targetComponent.transform.localScale)); } } } } } }
/// <summary>Return a writer to the pool.</summary> public static void Recycle(PooledNetworkWriter writer) { Pool.Return(writer); }
static ArraySegment <byte> CreateSpawnMessagePayload(bool isOwner, NetworkIdentity identity, PooledNetworkWriter ownerWriter, PooledNetworkWriter observersWriter) { // Only call OnSerializeAllSafely if there are NetworkBehaviours if (identity.NetworkBehaviours.Length == 0) { return(default);