完成阶段 4
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Protobuf;
|
||||
@@ -17,7 +18,7 @@ namespace Tests.EditMode.Network
|
||||
public void SendMessage_WithoutTarget_UsesDefaultSend()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var manager = new MessageManager(transport);
|
||||
var manager = new MessageManager(transport, new MainThreadNetworkDispatcher());
|
||||
var message = new Heartbeat();
|
||||
|
||||
manager.SendMessage(message, MessageType.Heartbeat);
|
||||
@@ -35,7 +36,7 @@ namespace Tests.EditMode.Network
|
||||
public void SendMessage_WithTarget_UsesExplicitSend()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var manager = new MessageManager(transport);
|
||||
var manager = new MessageManager(transport, new MainThreadNetworkDispatcher());
|
||||
var message = new LoginRequest
|
||||
{
|
||||
PlayerId = "player-1",
|
||||
@@ -57,7 +58,7 @@ namespace Tests.EditMode.Network
|
||||
public void BroadcastMessage_UsesBroadcastSend()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var manager = new MessageManager(transport);
|
||||
var manager = new MessageManager(transport, new MainThreadNetworkDispatcher());
|
||||
var message = new Heartbeat();
|
||||
|
||||
manager.BroadcastMessage(message, MessageType.Heartbeat);
|
||||
@@ -71,10 +72,10 @@ namespace Tests.EditMode.Network
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Receive_ValidEnvelope_DispatchesRegisteredHandler()
|
||||
public void Receive_ValidEnvelope_IsDeferredUntilDrain()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var manager = new MessageManager(transport);
|
||||
var manager = new MessageManager(transport, new MainThreadNetworkDispatcher());
|
||||
var handled = false;
|
||||
IPEndPoint receivedSender = null;
|
||||
byte[] receivedPayload = null;
|
||||
@@ -89,26 +90,33 @@ namespace Tests.EditMode.Network
|
||||
|
||||
transport.EmitReceive(BuildEnvelope(MessageType.Heartbeat, message), Sender);
|
||||
|
||||
Assert.That(handled, Is.False);
|
||||
Assert.That(manager.PendingMessageCount, Is.EqualTo(1));
|
||||
|
||||
manager.DrainPendingMessagesAsync().GetAwaiter().GetResult();
|
||||
|
||||
Assert.That(handled, Is.True);
|
||||
Assert.That(receivedSender, Is.EqualTo(Sender));
|
||||
Assert.That(receivedPayload, Is.EqualTo(message.ToByteArray()));
|
||||
Assert.That(manager.PendingMessageCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Receive_UnregisteredMessage_DoesNotThrow()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var manager = new MessageManager(transport);
|
||||
var manager = new MessageManager(transport, new MainThreadNetworkDispatcher());
|
||||
|
||||
Assert.DoesNotThrow(() =>
|
||||
transport.EmitReceive(BuildEnvelope(MessageType.Heartbeat, new Heartbeat()), Sender));
|
||||
Assert.That(manager.PendingMessageCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Receive_InvalidBytes_DoesNotBreakFollowingDispatch()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var manager = new MessageManager(transport);
|
||||
var manager = new MessageManager(transport, new MainThreadNetworkDispatcher());
|
||||
var handledCount = 0;
|
||||
|
||||
manager.RegisterHandler(MessageType.Heartbeat, (payload, sender) =>
|
||||
@@ -119,9 +127,37 @@ namespace Tests.EditMode.Network
|
||||
Assert.DoesNotThrow(() => transport.EmitReceive(new byte[] { 0x01, 0x02, 0x03 }, Sender));
|
||||
transport.EmitReceive(BuildEnvelope(MessageType.Heartbeat, new Heartbeat()), Sender);
|
||||
|
||||
Assert.That(handledCount, Is.EqualTo(0));
|
||||
Assert.That(manager.PendingMessageCount, Is.EqualTo(1));
|
||||
|
||||
manager.DrainPendingMessagesAsync().GetAwaiter().GetResult();
|
||||
|
||||
Assert.That(handledCount, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Receive_MultipleMessages_PreserveEnqueueOrder()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var manager = new MessageManager(transport, new MainThreadNetworkDispatcher());
|
||||
var handledSpeeds = new List<int>();
|
||||
|
||||
manager.RegisterHandler(MessageType.LoginRequest, (payload, sender) =>
|
||||
{
|
||||
handledSpeeds.Add(LoginRequest.Parser.ParseFrom(payload).Speed);
|
||||
});
|
||||
|
||||
transport.EmitReceive(BuildEnvelope(MessageType.LoginRequest, new LoginRequest { PlayerId = "a", Speed = 1 }), Sender);
|
||||
transport.EmitReceive(BuildEnvelope(MessageType.LoginRequest, new LoginRequest { PlayerId = "b", Speed = 2 }), Sender);
|
||||
|
||||
Assert.That(handledSpeeds, Is.Empty);
|
||||
Assert.That(manager.PendingMessageCount, Is.EqualTo(2));
|
||||
|
||||
manager.DrainPendingMessagesAsync().GetAwaiter().GetResult();
|
||||
|
||||
Assert.That(handledSpeeds, Is.EqualTo(new[] { 1, 2 }));
|
||||
}
|
||||
|
||||
private static byte[] BuildEnvelope(MessageType type, IMessage payload)
|
||||
{
|
||||
return new Envelope
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Protobuf;
|
||||
using Network.Defines;
|
||||
using Network.NetworkApplication;
|
||||
using Network.NetworkHost;
|
||||
using Network.NetworkTransport;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.EditMode.Network
|
||||
{
|
||||
public class SharedNetworkFoundationTests
|
||||
{
|
||||
private static readonly IPEndPoint Sender = new(IPAddress.Loopback, 9000);
|
||||
|
||||
[Test]
|
||||
public void SharedNetworkRuntime_UsesInjectedDispatcherForDeferredClientStyleDispatch()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var runtime = new SharedNetworkRuntime(transport, new MainThreadNetworkDispatcher());
|
||||
var handled = false;
|
||||
|
||||
runtime.MessageManager.RegisterHandler(MessageType.Heartbeat, (payload, sender) =>
|
||||
{
|
||||
handled = true;
|
||||
});
|
||||
|
||||
transport.EmitReceive(BuildEnvelope(MessageType.Heartbeat, new Heartbeat()), Sender);
|
||||
|
||||
Assert.That(handled, Is.False);
|
||||
Assert.That(runtime.MessageManager.PendingMessageCount, Is.EqualTo(1));
|
||||
|
||||
runtime.DrainPendingMessagesAsync().GetAwaiter().GetResult();
|
||||
|
||||
Assert.That(handled, Is.True);
|
||||
Assert.That(runtime.MessageManager.PendingMessageCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ServerNetworkHost_UsesImmediateDispatcherByDefault()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var host = new ServerNetworkHost(transport);
|
||||
var handled = false;
|
||||
|
||||
host.MessageManager.RegisterHandler(MessageType.Heartbeat, (payload, sender) =>
|
||||
{
|
||||
handled = true;
|
||||
});
|
||||
|
||||
transport.EmitReceive(BuildEnvelope(MessageType.Heartbeat, new Heartbeat()), Sender);
|
||||
|
||||
Assert.That(handled, Is.True);
|
||||
Assert.That(host.MessageManager.PendingMessageCount, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SharedHosts_PreserveEnvelopeProtocolContract()
|
||||
{
|
||||
var transport = new FakeTransport();
|
||||
var runtime = new SharedNetworkRuntime(transport, new ImmediateNetworkMessageDispatcher());
|
||||
var message = new LoginRequest
|
||||
{
|
||||
PlayerId = "shared-player",
|
||||
Speed = 7
|
||||
};
|
||||
|
||||
runtime.MessageManager.SendMessage(message, MessageType.LoginRequest, Sender);
|
||||
|
||||
var envelope = Envelope.Parser.ParseFrom(transport.LastSendToData);
|
||||
Assert.That(envelope.Type, Is.EqualTo((int)MessageType.LoginRequest));
|
||||
Assert.That(LoginRequest.Parser.ParseFrom(envelope.Payload).PlayerId, Is.EqualTo("shared-player"));
|
||||
Assert.That(LoginRequest.Parser.ParseFrom(envelope.Payload).Speed, Is.EqualTo(7));
|
||||
Assert.That(transport.LastSendTarget, Is.EqualTo(Sender));
|
||||
}
|
||||
|
||||
private static byte[] BuildEnvelope(MessageType type, IMessage payload)
|
||||
{
|
||||
return new Envelope
|
||||
{
|
||||
Type = (int)type,
|
||||
Payload = payload.ToByteString()
|
||||
}.ToByteArray();
|
||||
}
|
||||
|
||||
private sealed class FakeTransport : ITransport
|
||||
{
|
||||
public byte[] LastSendToData { get; private set; }
|
||||
|
||||
public IPEndPoint LastSendTarget { get; private set; }
|
||||
|
||||
public event Action<byte[], IPEndPoint> OnReceive;
|
||||
|
||||
public Task StartAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
public void Send(byte[] data)
|
||||
{
|
||||
}
|
||||
|
||||
public void SendTo(byte[] data, IPEndPoint target)
|
||||
{
|
||||
LastSendToData = Copy(data);
|
||||
LastSendTarget = target;
|
||||
}
|
||||
|
||||
public void SendToAll(byte[] data)
|
||||
{
|
||||
}
|
||||
|
||||
public void EmitReceive(byte[] data, IPEndPoint sender)
|
||||
{
|
||||
OnReceive?.Invoke(Copy(data), sender);
|
||||
}
|
||||
|
||||
private static byte[] Copy(byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var copy = new byte[data.Length];
|
||||
Array.Copy(data, copy, data.Length);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb9381c1693943c79457fc3fe9174e4a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Network.NetworkTransport;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Tests.EditMode.Network
|
||||
{
|
||||
public class TransportArchitectureTests
|
||||
{
|
||||
[Test]
|
||||
public void KcpTransport_ImplementsITransport()
|
||||
{
|
||||
Assert.That(typeof(ITransport).IsAssignableFrom(typeof(KcpTransport)), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReliableUdpTransport_IsNotAvailable()
|
||||
{
|
||||
var reliableUdpTransportType = AppDomain.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.Select(assembly => assembly.GetType("Network.NetworkTransport.ReliableUdpTransport", throwOnError: false))
|
||||
.FirstOrDefault(type => type != null);
|
||||
|
||||
Assert.That(reliableUdpTransportType, Is.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acd10ddd335680e44bce8246024e7f6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user