internal void Update() { // should we still process a connected event? if (connectedEventPending) { connectedEventPending = false; NetworkClient.OnConnectedEvent?.Invoke(this); } // 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> segment = writer.ToArraySegment(); //Debug.Log("Dequeue " + BitConverter.ToString(segment.Array, segment.Offset, segment.Count)); TransportReceive(segment, Channels.DefaultReliable); NetworkWriterPool.Recycle(writer); } // should we still process a disconnected event? if (disconnectedEventPending) { disconnectedEventPending = false; NetworkClient.OnDisconnectedEvent?.Invoke(this); } }
// batch as many messages as possible into writer // returns true if any batch was made. public bool MakeNextBatch(NetworkWriter writer) { // 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!"); } // 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 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 && hasAuthority) { // 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.ToArraySegment()); 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 (!hasAuthority) { // 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); } 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)); } } } } }
public virtual bool ClientSend(int channelId, NetworkWriter data) { bool result = ClientSend(channelId, data.ToArray()); NetworkWriterPool.Recycle(data); return(result); }
public virtual bool ServerSend(int connectionId, int channelId, NetworkWriter data) { bool result = ServerSend(connectionId, channelId, data.ToArray()); NetworkWriterPool.Recycle(data); return(result); }
public static bool SendToReady <T>(NetworkIdentity identity, T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { if (LogFilter.Debug) { Debug.Log("Server.SendToReady msgType:" + typeof(T)); } if (identity != null && identity.observers != null) { // pack message into byte[] once NetworkWriter bytes = MessagePacker.PackWriter(msg); bytes.recycleCount = 1; bool result = true; foreach (KeyValuePair <int, NetworkConnection> kvp in identity.observers) { if (kvp.Value.isReady) { bytes.recycleCount++; result &= kvp.Value.SendWriter(bytes, channelId); } } NetworkWriterPool.Recycle(bytes); return(result); } return(false); }
internal static void Update() { // local or remote connection? if (isLocalClient) { // process internal messages so they are applied at the correct time while (localClientPacketQueue.Count > 0) { BufferHolder packet = localClientPacketQueue.Dequeue(); if (packet.writer) { NetworkWriter writer = (NetworkWriter)packet.data; OnDataReceived(writer.ToArraySegment()); NetworkWriterPool.Recycle(writer); } else { OnDataReceived(new ArraySegment <byte>((byte[])packet.data)); } } } else { // only update things while connected if (active && connectState == ConnectState.Connected) { NetworkTime.UpdateClient(); } } }
protected void SendRPCInternal(Type invokeClass, string rpcName, NetworkWriter writer, int channelId) { // this was in Weaver before if (!NetworkServer.active) { Debug.LogError("RPC Function " + rpcName + " called on Client."); return; } // This cannot use NetworkServer.active, as that is not specific to this object. if (!isServer) { Debug.LogWarning("ClientRpc " + rpcName + " called on un-spawned object: " + name); return; } // construct the message RpcMessage message = new RpcMessage { netId = netId, componentIndex = ComponentIndex, functionHash = GetMethodHash(invokeClass, rpcName), // type+func so Inventory.RpcUse != Equipment.RpcUse payload = writer.ToArraySegment() // segment to avoid reader allocations }; NetworkServer.SendToReady(netIdentity, message, channelId); NetworkWriterPool.Recycle(writer); }
void FixedUpdate() { if (!sendMessagesAllowed) { return; } CheckSendRate(); for (int i = 0; i < animator.layerCount; i++) { int stateHash; float normalizedTime; if (!CheckAnimStateChanged(out stateHash, out normalizedTime, i)) { continue; } NetworkWriter writer = NetworkWriterPool.GetWriter(); WriteParameters(writer); SendAnimationMessage(stateHash, normalizedTime, i, writer.ToArray()); NetworkWriterPool.Recycle(writer); } }
protected void SendCommandInternal(Type invokeClass, string cmdName, NetworkWriter writer, int channelId) { // this was in Weaver before // NOTE: we could remove this later to allow calling Cmds on Server // to avoid Wrapper functions. a lot of people requested this. if (!NetworkClient.active) { Debug.LogError("Command Function " + cmdName + " called on server without an active client."); return; } // local players can always send commands, regardless of authority, other objects must have authority. if (!(isLocalPlayer || hasAuthority)) { Debug.LogWarning("Trying to send command for object without authority."); return; } if (ClientScene.readyConnection == null) { Debug.LogError("Send command attempted with no client running [client=" + connectionToServer + "]."); return; } // construct the message CommandMessage message = new CommandMessage { netId = netId, componentIndex = ComponentIndex, functionHash = GetMethodHash(invokeClass, cmdName), // type+func so Inventory.RpcUse != Equipment.RpcUse payload = writer.ToArraySegment() // segment to avoid reader allocations }; ClientScene.readyConnection.Send(message, channelId); NetworkWriterPool.Recycle(writer); }
// 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; // 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); }
// 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, channelId, writer.ToArraySegment()); writer.SetLength(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, channelId, writer.ToArraySegment()); writer.SetLength(0); } } // reset send time for this channel's batch batch.lastSendTime = NetworkTime.time; }
public bool InvokeHandler <T>(T msg) where T : IMessageBase { NetworkWriter writer = MessagePacker.PackWriter(msg); NetworkReader reader = NetworkReaderPool.GetPooledReader(writer.ToArraySegment()); bool result = InvokeHandler(MessagePacker.GetId <T>(), reader); NetworkReaderPool.Recycle(reader); NetworkWriterPool.Recycle(writer); return(result); }
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); }
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); }
/// <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 { 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); }
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); }
public static byte[] PackMessage(int msgType, MessageBase msg) { NetworkWriter packWriter = NetworkWriterPool.GetPooledWriter(); // write message type packWriter.Write((short)msgType); // serialize message into writer msg.Serialize(packWriter); // return byte[] byte[] data = packWriter.ToArray(); NetworkWriterPool.Recycle(packWriter); return(data); }
// pack message before sending public static byte[] Pack <T>(T message) where T : IMessageBase { NetworkWriter packWriter = NetworkWriterPool.GetPooledWriter(); // write message type int msgType = GetId <T>(); packWriter.Write((ushort)msgType); // serialize message into writer message.Serialize(packWriter); // return byte[] byte[] data = packWriter.ToArray(); NetworkWriterPool.Recycle(packWriter); return(data); }
/// <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); }
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.WriteInt16((short)msgType); // serialize message into writer msg.Serialize(writer); // return byte[] return(writer.ToArray()); } finally { NetworkWriterPool.Recycle(writer); } }
protected void SendEventInternal(Type invokeClass, string eventName, NetworkWriter writer, int channelId) { if (!NetworkServer.active) { Debug.LogWarning("SendEvent no server?"); return; } // construct the message SyncEventMessage message = new SyncEventMessage { netId = netId, componentIndex = ComponentIndex, functionHash = GetMethodHash(invokeClass, eventName), // type+func so Inventory.RpcUse != Equipment.RpcUse payload = writer.ToArraySegment() // segment to avoid reader allocations }; NetworkServer.SendToReady(netIdentity, message, channelId); NetworkWriterPool.Recycle(writer); }
void FixedUpdate() { if (!sendMessagesAllowed) { return; } CheckSendRate(); if (!CheckAnimStateChanged(out int stateHash, out float normalizedTime)) { return; } NetworkWriter writer = NetworkWriterPool.GetPooledWriter(); WriteParameters(writer); SendAnimationMessage(stateHash, normalizedTime, writer.ToArraySegment()); 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); } }
protected void SendTargetRPCInternal(NetworkConnection conn, Type invokeClass, string rpcName, NetworkWriter writer, int channelId) { // this was in Weaver before if (!NetworkServer.active) { Debug.LogError("TargetRPC Function " + rpcName + " called on client."); return; } // connection parameter is optional. assign if null. if (conn == null) { conn = connectionToClient; } // this was in Weaver before if (conn is ULocalConnectionToServer) { Debug.LogError("TargetRPC Function " + rpcName + " called on connection to server"); return; } // This cannot use NetworkServer.active, as that is not specific to this object. if (!isServer) { Debug.LogWarning("TargetRpc " + rpcName + " called on un-spawned object: " + name); return; } // construct the message RpcMessage message = new RpcMessage { netId = netId, componentIndex = ComponentIndex, functionHash = GetMethodHash(invokeClass, rpcName), // type+func so Inventory.RpcUse != Equipment.RpcUse payload = writer.ToArraySegment() // segment to avoid reader allocations }; conn.Send(message, channelId); NetworkWriterPool.Recycle(writer); }
internal static void Update() { // local or remote connection? if (isLocalClient) { // process internal messages so they are applied at the correct time while (localClientPacketQueue.Count > 0) { NetworkWriter writer = localClientPacketQueue.Dequeue(); // TODO avoid serializing and deserializng the message // just pass it as is OnDataReceived(writer.ToArraySegment(), Channels.DefaultReliable); NetworkWriterPool.Recycle(writer); } } else { // only update things while connected if (active && connectState == ConnectState.Connected) { NetworkTime.UpdateClient(); } } }
public void Dispose() { NetworkWriterPool.Recycle(this); }