breaking: fix: #3652 host [Command]s are now simulated over a message queue instead of invoking them directly (credits: Brian B.) (#3653)

* comment

* fix: host [Command]s are now simulated over a message queue instead of invoking them directly (credits: Brian B.)
This commit is contained in:
mischa 2023-11-17 10:34:20 +01:00 committed by GitHub
parent 8ec937dfa6
commit d4c0f20827
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 15 deletions

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
namespace Mirror namespace Mirror
{ {
@ -8,6 +9,9 @@ public class LocalConnectionToClient : NetworkConnectionToClient
{ {
internal LocalConnectionToServer connectionToServer; internal LocalConnectionToServer connectionToServer;
// packet queue
internal readonly Queue<NetworkWriterPooled> queue = new Queue<NetworkWriterPooled>();
public LocalConnectionToClient() : base(LocalConnectionId) {} public LocalConnectionToClient() : base(LocalConnectionId) {}
public override string address => "localhost"; public override string address => "localhost";
@ -17,6 +21,7 @@ internal override void Send(ArraySegment<byte> segment, int channelId = Channels
// instead of invoking it directly, we enqueue and process next update. // instead of invoking it directly, we enqueue and process next update.
// this way we can simulate a similar call flow as with remote clients. // this way we can simulate a similar call flow as with remote clients.
// the closer we get to simulating host as remote, the better! // the closer we get to simulating host as remote, the better!
// both directions do this, so [Command] and [Rpc] behave the same way.
//Debug.Log($"Enqueue {BitConverter.ToString(segment.Array, segment.Offset, segment.Count)}"); //Debug.Log($"Enqueue {BitConverter.ToString(segment.Array, segment.Offset, segment.Count)}");
NetworkWriterPooled writer = NetworkWriterPool.Get(); NetworkWriterPooled writer = NetworkWriterPool.Get();
@ -30,6 +35,35 @@ internal override void Send(ArraySegment<byte> segment, int channelId = Channels
// don't ping host client in host mode // don't ping host client in host mode
protected override void UpdatePing() {} protected override void UpdatePing() {}
internal override void Update()
{
base.Update();
// 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))
{
NetworkServer.OnTransportData(connectionId, batchWriter.ToArraySegment(), Channels.Reliable);
}
}
NetworkWriterPool.Return(writer);
}
}
internal void DisconnectInternal() internal void DisconnectInternal()
{ {
// set not ready and handle clientscene disconnect in any case // set not ready and handle clientscene disconnect in any case

View File

@ -28,22 +28,15 @@ internal override void Send(ArraySegment<byte> segment, int channelId = Channels
return; return;
} }
// OnTransportData assumes batching. // instead of invoking it directly, we enqueue and process next update.
// so let's make a batch with proper timestamp prefix. // this way we can simulate a similar call flow as with remote clients.
Batcher batcher = GetBatchForChannelId(channelId); // the closer we get to simulating host as remote, the better!
batcher.AddMessage(segment, NetworkTime.localTime); // both directions do this, so [Command] and [Rpc] behave the same way.
// flush it to the server's OnTransportData immediately. //Debug.Log($"Enqueue {BitConverter.ToString(segment.Array, segment.Offset, segment.Count)}");
// local connection to server always invokes immediately. NetworkWriterPooled writer = NetworkWriterPool.Get();
using (NetworkWriterPooled writer = NetworkWriterPool.Get()) writer.WriteBytes(segment.Array, segment.Offset, segment.Count);
{ connectionToClient.queue.Enqueue(writer);
// 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.");
}
} }
internal override void Update() internal override void Update()