12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- // batching functionality encapsulated into one class.
- // -> less complexity
- // -> easy to test
- //
- // IMPORTANT: we use THRESHOLD batching, not MAXED SIZE batching.
- // see threshold comments below.
- //
- // includes timestamp for tick batching.
- // -> allows NetworkTransform etc. to use timestamp without including it in
- // every single message
- using System;
- using System.Collections.Generic;
- namespace Mirror
- {
- public class Batcher
- {
- // batching threshold instead of max size.
- // -> small messages are fit into threshold sized batches
- // -> messages larger than threshold are single batches
- //
- // in other words, we fit up to 'threshold' but still allow larger ones
- // for two reasons:
- // 1.) data races: skipping batching for larger messages would send a
- // large spawn message immediately, while others are batched and
- // only flushed at the end of the frame
- // 2) timestamp batching: if each batch is expected to contain a
- // timestamp, then large messages have to be a batch too. otherwise
- // they would not contain a timestamp
- readonly int threshold;
- // TimeStamp header size for those who need it
- public const int HeaderSize = sizeof(double);
- // batched messages
- // IMPORTANT: we queue the serialized messages!
- // queueing NetworkMessage would box and allocate!
- Queue<PooledNetworkWriter> messages = new Queue<PooledNetworkWriter>();
- public Batcher(int threshold)
- {
- this.threshold = threshold;
- }
- // 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);
- }
- // 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;
- }
- }
- }
|