// Send stage two: serialized NetworkMessage as ArraySegment<byte> internal override void Send(ArraySegment <byte> segment, int channelId = Channels.Reliable) { if (segment.Count == 0) { Debug.LogError("LocalConnection.SendBytes cannot send zero bytes"); return; } // OnTransportData assumes batching. // so let's make a batch with proper timestamp prefix. Batcher batcher = GetBatchForChannelId(channelId); batcher.AddMessage(segment, NetworkTime.localTime); // flush it to the server's OnTransportData immediately. // local connection to server always invokes immediately. using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { // make a batch with our local time (double precision) if (batcher.GetBatch(writer)) { NetworkServer.OnTransportData(connectionId, writer.ToArraySegment(), channelId); } else { Debug.LogError("Local connection failed to make batch. This should never happen."); } } }
// flush batched messages at the end of every Update. internal virtual void Update() { // go through batches for all channels foreach (KeyValuePair <int, Batcher> kvp in batches) { // make and send as many batches as necessary from the stored // messages. Batcher batcher = kvp.Value; using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { // make a batch with our local time (double precision) while (batcher.MakeNextBatch(writer, NetworkTime.localTime)) { // validate packet before handing the batch to the // transport. this guarantees that we always stay // within transport's max message size limit. // => just in case transport forgets to check it // => just in case mirror miscalulated it etc. ArraySegment <byte> segment = writer.ToArraySegment(); if (ValidatePacketSize(segment, kvp.Key)) { // send to transport SendToTransport(segment, kvp.Key); //UnityEngine.Debug.Log($"sending batch of {writer.Position} bytes for channel={kvp.Key} connId={connectionId}"); // reset writer for each new batch writer.Position = 0; } } } } }
// helper function to start reading a batch. void StartReadingBatch(NetworkWriterPooled batch) { // point reader to it reader.SetBuffer(batch.ToArraySegment()); // read remote timestamp (double) // -> AddBatch quarantees that we have at least 8 bytes to read readerRemoteTimeStamp = reader.ReadDouble(); }
public void Send <T>(T message, int channelId = Channels.Reliable) where T : struct, NetworkMessage { using (NetworkWriterPooled writer = NetworkWriterPool.Get()) { // pack message and send allocation free MessagePacking.Pack(message, writer); NetworkDiagnostics.OnSend(message, channelId, writer.Position, 1); Send(writer.ToArraySegment(), channelId); } }
// helper function to copy a batch to writer and return it to pool static void CopyAndReturn(NetworkWriterPooled batch, NetworkWriter writer) { // make sure the writer is fresh to avoid uncertain situations if (writer.Position != 0) { throw new ArgumentException($"GetBatch needs a fresh writer!"); } // copy to the target writer ArraySegment <byte> segment = batch.ToArraySegment(); writer.WriteBytes(segment.Array, segment.Offset, segment.Count); // return batch to pool for reuse NetworkWriterPool.Return(batch); }
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 NetworkWriterPooled 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, NetworkTime.localTime); using (NetworkWriterPooled batchWriter = NetworkWriterPool.Get()) { // make a batch with our local time (double precision) if (batcher.GetBatch(batchWriter)) { NetworkClient.OnTransportData(batchWriter.ToArraySegment(), Channels.Reliable); } } NetworkWriterPool.Return(writer); } // should we still process a disconnected event? if (disconnectedEventPending) { disconnectedEventPending = false; NetworkClient.OnDisconnectedEvent?.Invoke(); } }
// 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) NetworkWriterPooled message = messages.Dequeue(); ArraySegment <byte> segment = message.ToArraySegment(); writer.WriteBytes(segment.Array, segment.Offset, segment.Count); // return the writer to pool NetworkWriterPool.Return(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); }