This commit is contained in:
mischa 2024-07-04 09:59:24 +02:00
parent 49ad787d7c
commit 3cbaa58caf
23 changed files with 119 additions and 78 deletions

View File

@ -12,9 +12,7 @@ public class LocalConnectionToClient : NetworkConnectionToClient
// packet queue // packet queue
internal readonly Queue<NetworkWriterPooled> queue = new Queue<NetworkWriterPooled>(); internal readonly Queue<NetworkWriterPooled> queue = new Queue<NetworkWriterPooled>();
public LocalConnectionToClient() : base(LocalConnectionId) {} public LocalConnectionToClient() : base(LocalConnectionId, "localhost") {}
public override string address => "localhost";
internal override void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable) internal override void Send(ArraySegment<byte> segment, int channelId = Channels.Reliable)
{ {

View File

@ -7,6 +7,8 @@ namespace Mirror
{ {
public class NetworkConnectionToClient : NetworkConnection public class NetworkConnectionToClient : NetworkConnection
{ {
// connection address, passed from transport
// rpcs are collected in a buffer, and then flushed out together. // rpcs are collected in a buffer, and then flushed out together.
// this way we don't need one NetworkMessage per rpc. // this way we don't need one NetworkMessage per rpc.
// => prepares for LocalWorldState as well. // => prepares for LocalWorldState as well.
@ -14,7 +16,7 @@ public class NetworkConnectionToClient : NetworkConnection
readonly NetworkWriter reliableRpcs = new NetworkWriter(); readonly NetworkWriter reliableRpcs = new NetworkWriter();
readonly NetworkWriter unreliableRpcs = new NetworkWriter(); readonly NetworkWriter unreliableRpcs = new NetworkWriter();
public virtual string address => Transport.active.ServerGetClientAddress(connectionId); public string address { get; private set; }
/// <summary>NetworkIdentities that this connection can see</summary> /// <summary>NetworkIdentities that this connection can see</summary>
// TODO move to server's NetworkConnectionToClient? // TODO move to server's NetworkConnectionToClient?
@ -50,9 +52,29 @@ public class NetworkConnectionToClient : NetworkConnection
/// <summary>Round trip time (in seconds) that it takes a message to go server->client->server.</summary> /// <summary>Round trip time (in seconds) that it takes a message to go server->client->server.</summary>
public double rtt => _rtt.Value; public double rtt => _rtt.Value;
public NetworkConnectionToClient(int networkConnectionId, string address)
: base(networkConnectionId)
{
this.address = address;
// initialize EMA with 'emaDuration' seconds worth of history.
// 1 second holds 'sendRate' worth of values.
// multiplied by emaDuration gives n-seconds.
driftEma = new ExponentialMovingAverage(NetworkServer.sendRate * NetworkClient.snapshotSettings.driftEmaDuration);
deliveryTimeEma = new ExponentialMovingAverage(NetworkServer.sendRate * NetworkClient.snapshotSettings.deliveryTimeEmaDuration);
// buffer limit should be at least multiplier to have enough in there
snapshotBufferSizeLimit = Mathf.Max((int)NetworkClient.snapshotSettings.bufferTimeMultiplier, snapshotBufferSizeLimit);
}
// keep the old contstructor for a while in order to not break all projects.
// DEPRECATED 2024-05-16
[Obsolete("'new NetworkConnection(connectionId)' constructor was changed to 'new NetworkConnection(connectionId, address)'")]
public NetworkConnectionToClient(int networkConnectionId) public NetworkConnectionToClient(int networkConnectionId)
: base(networkConnectionId) : base(networkConnectionId)
{ {
this.address = Transport.active.ServerGetClientAddress(connectionId);
// initialize EMA with 'emaDuration' seconds worth of history. // initialize EMA with 'emaDuration' seconds worth of history.
// 1 second holds 'sendRate' worth of values. // 1 second holds 'sendRate' worth of values.
// multiplied by emaDuration gives n-seconds. // multiplied by emaDuration gives n-seconds.

View File

@ -634,8 +634,9 @@ public static void SendToReadyObservers<T>(NetworkIdentity identity, T message,
} }
// transport events //////////////////////////////////////////////////// // transport events ////////////////////////////////////////////////////
// called by transport // called by transport.
static void OnTransportConnected(int connectionId) // address is the (IP) address without port. useful for IP bans etc.
static void OnTransportConnected(int connectionId, string address)
{ {
// Debug.Log($"Server accepted client:{connectionId}"); // Debug.Log($"Server accepted client:{connectionId}");
@ -665,7 +666,7 @@ static void OnTransportConnected(int connectionId)
if (connections.Count < maxConnections) if (connections.Count < maxConnections)
{ {
// add connection // add connection
NetworkConnectionToClient conn = new NetworkConnectionToClient(connectionId); NetworkConnectionToClient conn = new NetworkConnectionToClient(connectionId, address);
OnConnected(conn); OnConnected(conn);
} }
else else

View File

@ -3,6 +3,9 @@
// Connecting: // Connecting:
// * Transports are responsible to call either OnConnected || OnDisconnected // * Transports are responsible to call either OnConnected || OnDisconnected
// in a certain time after a Connect was called. It can not end in limbo. // in a certain time after a Connect was called. It can not end in limbo.
// * OnConnected should always pass the connection address.
// This makes threaded transports easier.
// Otherwise we would need to keep a main thread connections copy.
// //
// Disconnecting: // Disconnecting:
// * Connections might disconnect voluntarily by the other end. // * Connections might disconnect voluntarily by the other end.
@ -67,7 +70,8 @@ public abstract class Transport : MonoBehaviour
// server ////////////////////////////////////////////////////////////// // server //////////////////////////////////////////////////////////////
/// <summary>Called by Transport when a new client connected to the server.</summary> /// <summary>Called by Transport when a new client connected to the server.</summary>
public Action<int> OnServerConnected; // parameters: <connectionId, address> to pass address directly instead of having a separate ServerGetClientAddress() function.
public Action<int, string> OnServerConnected;
/// <summary>Called by Transport when the server received a message from a client.</summary> /// <summary>Called by Transport when the server received a message from a client.</summary>
public Action<int, ArraySegment<byte>, int> OnServerDataReceived; public Action<int, ArraySegment<byte>, int> OnServerDataReceived;
@ -131,6 +135,8 @@ public virtual void ClientConnect(Uri uri)
/// <summary>Get a client's address on the server.</summary> /// <summary>Get a client's address on the server.</summary>
// Can be useful for Game Master IP bans etc. // Can be useful for Game Master IP bans etc.
// DEPRECATED 2024-05-16
[Obsolete("Transport.ServerGetClientAddress() was deprecated. Each connection's address is now passed only once in OnServerConnected instead.")]
public abstract string ServerGetClientAddress(int connectionId); public abstract string ServerGetClientAddress(int connectionId);
/// <summary>Stop listening and disconnect all connections.</summary> /// <summary>Stop listening and disconnect all connections.</summary>

View File

@ -4,8 +4,7 @@ namespace Mirror.Tests
{ {
public class FakeNetworkConnectionToClient : NetworkConnectionToClient public class FakeNetworkConnectionToClient : NetworkConnectionToClient
{ {
public FakeNetworkConnectionToClient() : base(1) {} public FakeNetworkConnectionToClient() : base(1, "localhost") {}
public override string address => "Test";
public override void Disconnect() {} public override void Disconnect() {}
internal override void Send(ArraySegment<byte> segment, int channelId = 0) {} internal override void Send(ArraySegment<byte> segment, int channelId = 0) {}
} }

View File

@ -201,7 +201,7 @@ public override void ServerEarlyUpdate()
case EventType.Connected: case EventType.Connected:
Debug.Log("MemoryTransport Server Message: Connected"); Debug.Log("MemoryTransport Server Message: Connected");
// event might be null in tests if no NetworkClient is used. // event might be null in tests if no NetworkClient is used.
OnServerConnected?.Invoke(message.connectionId); OnServerConnected?.Invoke(message.connectionId, "localhost");
break; break;
case EventType.Data: case EventType.Data:
Debug.Log($"MemoryTransport Server Message: Data: {BitConverter.ToString(message.data)}"); Debug.Log($"MemoryTransport Server Message: Data: {BitConverter.ToString(message.data)}");

View File

@ -21,7 +21,7 @@ public override void SetUp()
// A with connectionId = 0x0A, netId = 0xAA // A with connectionId = 0x0A, netId = 0xAA
CreateNetworked(out gameObjectA, out identityA); CreateNetworked(out gameObjectA, out identityA);
connectionA = new NetworkConnectionToClient(0x0A); connectionA = new NetworkConnectionToClient(0x0A, "");
connectionA.isAuthenticated = true; connectionA.isAuthenticated = true;
connectionA.isReady = true; connectionA.isReady = true;
connectionA.identity = identityA; connectionA.identity = identityA;
@ -29,7 +29,7 @@ public override void SetUp()
// B // B
CreateNetworked(out gameObjectB, out identityB); CreateNetworked(out gameObjectB, out identityB);
connectionB = new NetworkConnectionToClient(0x0B); connectionB = new NetworkConnectionToClient(0x0B, "");
connectionB.isAuthenticated = true; connectionB.isAuthenticated = true;
connectionB.isReady = true; connectionB.isReady = true;
connectionB.identity = identityB; connectionB.identity = identityB;

View File

@ -34,7 +34,7 @@ public override void TearDown()
public void Send_BatchesUntilUpdate() public void Send_BatchesUntilUpdate()
{ {
// create connection and send // create connection and send
NetworkConnectionToClient connection = new NetworkConnectionToClient(42); NetworkConnectionToClient connection = new NetworkConnectionToClient(42, "");
NetworkTime.PingInterval = float.MaxValue; // disable ping for this test NetworkTime.PingInterval = float.MaxValue; // disable ping for this test
byte[] message = {0x01, 0x02}; byte[] message = {0x01, 0x02};
connection.Send(new ArraySegment<byte>(message)); connection.Send(new ArraySegment<byte>(message));
@ -63,7 +63,7 @@ public void SendBatchingResetsPreviousWriter()
const int BatchHeader = 8; const int BatchHeader = 8;
// create connection // create connection
NetworkConnectionToClient connection = new NetworkConnectionToClient(42); NetworkConnectionToClient connection = new NetworkConnectionToClient(42, "");
NetworkTime.PingInterval = float.MaxValue; // disable ping for this test NetworkTime.PingInterval = float.MaxValue; // disable ping for this test
// send and update big message // send and update big message

View File

@ -123,11 +123,11 @@ public void RemoveObserver()
identity.OnStartServer(); identity.OnStartServer();
// add an observer connection // add an observer connection
NetworkConnectionToClient connection = new NetworkConnectionToClient(42); NetworkConnectionToClient connection = new NetworkConnectionToClient(42, "");
identity.observers[connection.connectionId] = connection; identity.observers[connection.connectionId] = connection;
// RemoveObserver with invalid connection should do nothing // RemoveObserver with invalid connection should do nothing
identity.RemoveObserver(new NetworkConnectionToClient(43)); identity.RemoveObserver(new NetworkConnectionToClient(43, ""));
Assert.That(identity.observers.Count, Is.EqualTo(1)); Assert.That(identity.observers.Count, Is.EqualTo(1));
// RemoveObserver with existing connection should remove it // RemoveObserver with existing connection should remove it
@ -324,7 +324,7 @@ public void AssignAndRemoveClientAuthority()
// another connection // another connection
// error log is expected // error log is expected
LogAssert.ignoreFailingMessages = true; LogAssert.ignoreFailingMessages = true;
result = identity.AssignClientAuthority(new NetworkConnectionToClient(43)); result = identity.AssignClientAuthority(new NetworkConnectionToClient(43, ""));
LogAssert.ignoreFailingMessages = false; LogAssert.ignoreFailingMessages = false;
Assert.That(result, Is.False); Assert.That(result, Is.False);
Assert.That(identity.connectionToClient, Is.EqualTo(owner)); Assert.That(identity.connectionToClient, Is.EqualTo(owner));
@ -552,8 +552,8 @@ public void AddObserver()
CreateNetworked(out GameObject _, out NetworkIdentity identity); CreateNetworked(out GameObject _, out NetworkIdentity identity);
// create some connections // create some connections
NetworkConnectionToClient connection1 = new NetworkConnectionToClient(42); NetworkConnectionToClient connection1 = new NetworkConnectionToClient(42, "");
NetworkConnectionToClient connection2 = new NetworkConnectionToClient(43); NetworkConnectionToClient connection2 = new NetworkConnectionToClient(43, "");
// call AddObservers // call AddObservers
identity.AddObserver(connection1); identity.AddObserver(connection1);
@ -565,7 +565,7 @@ public void AddObserver()
Assert.That(identity.observers[connection2.connectionId], Is.EqualTo(connection2)); Assert.That(identity.observers[connection2.connectionId], Is.EqualTo(connection2));
// adding a duplicate connectionId shouldn't overwrite the original // adding a duplicate connectionId shouldn't overwrite the original
NetworkConnectionToClient duplicate = new NetworkConnectionToClient(connection1.connectionId); NetworkConnectionToClient duplicate = new NetworkConnectionToClient(connection1.connectionId, "");
identity.AddObserver(duplicate); identity.AddObserver(duplicate);
Assert.That(identity.observers.Count, Is.EqualTo(2)); Assert.That(identity.observers.Count, Is.EqualTo(2));
Assert.That(identity.observers.ContainsKey(connection1.connectionId)); Assert.That(identity.observers.ContainsKey(connection1.connectionId));
@ -583,8 +583,8 @@ public void ClearObservers()
identity.OnStartServer(); identity.OnStartServer();
// add some observers // add some observers
identity.observers[42] = new NetworkConnectionToClient(42); identity.observers[42] = new NetworkConnectionToClient(42, "");
identity.observers[43] = new NetworkConnectionToClient(43); identity.observers[43] = new NetworkConnectionToClient(43, "");
// call ClearObservers // call ClearObservers
identity.ClearObservers(); identity.ClearObservers();
@ -632,9 +632,9 @@ public void ResetState()
identity.isClient = true; identity.isClient = true;
// creates .observers and generates a netId // creates .observers and generates a netId
identity.OnStartServer(); identity.OnStartServer();
identity.connectionToClient = new NetworkConnectionToClient(1); identity.connectionToClient = new NetworkConnectionToClient(1, "");
identity.connectionToServer = new NetworkConnectionToServer(); identity.connectionToServer = new NetworkConnectionToServer();
identity.observers[43] = new NetworkConnectionToClient(2); identity.observers[43] = new NetworkConnectionToClient(2, "");
// mark for reset and reset // mark for reset and reset
identity.ResetState(); identity.ResetState();

View File

@ -67,11 +67,11 @@ public void MaxConnections()
Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));
// connect first: should work // connect first: should work
transport.OnServerConnected.Invoke(42); transport.OnServerConnected.Invoke(42, "");
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
// connect second: should fail // connect second: should fail
transport.OnServerConnected.Invoke(43); transport.OnServerConnected.Invoke(43, "");
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
} }
@ -84,7 +84,7 @@ public void OnConnectedEventCalled()
// listen & connect // listen & connect
NetworkServer.Listen(1); NetworkServer.Listen(1);
transport.OnServerConnected.Invoke(42); transport.OnServerConnected.Invoke(42, "");
Assert.That(connectCalled, Is.True); Assert.That(connectCalled, Is.True);
} }
@ -97,7 +97,7 @@ public void OnDisconnectedEventCalled()
// listen & connect // listen & connect
NetworkServer.Listen(1); NetworkServer.Listen(1);
transport.OnServerConnected.Invoke(42); transport.OnServerConnected.Invoke(42, "");
// disconnect // disconnect
transport.OnServerDisconnected.Invoke(42); transport.OnServerDisconnected.Invoke(42);
@ -112,12 +112,12 @@ public void ConnectionsDict()
Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));
// connect first // connect first
transport.OnServerConnected.Invoke(42); transport.OnServerConnected.Invoke(42, "");
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
Assert.That(NetworkServer.connections.ContainsKey(42), Is.True); Assert.That(NetworkServer.connections.ContainsKey(42), Is.True);
// connect second // connect second
transport.OnServerConnected.Invoke(43); transport.OnServerConnected.Invoke(43, "");
Assert.That(NetworkServer.connections.Count, Is.EqualTo(2)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(2));
Assert.That(NetworkServer.connections.ContainsKey(43), Is.True); Assert.That(NetworkServer.connections.ContainsKey(43), Is.True);
@ -144,7 +144,7 @@ public void OnConnectedOnlyAllowsNonZeroConnectionIds()
// connect with connectionId == 0 should fail // connect with connectionId == 0 should fail
// (it will show an error message, which is expected) // (it will show an error message, which is expected)
LogAssert.ignoreFailingMessages = true; LogAssert.ignoreFailingMessages = true;
transport.OnServerConnected.Invoke(0); transport.OnServerConnected.Invoke(0, "");
Assert.That(NetworkServer.connections.Count, Is.EqualTo(0)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));
LogAssert.ignoreFailingMessages = false; LogAssert.ignoreFailingMessages = false;
} }
@ -156,12 +156,12 @@ public void ConnectDuplicateConnectionIds()
NetworkServer.Listen(2); NetworkServer.Listen(2);
// connect first // connect first
transport.OnServerConnected.Invoke(42); transport.OnServerConnected.Invoke(42, "");
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
NetworkConnectionToClient original = NetworkServer.connections[42]; NetworkConnectionToClient original = NetworkServer.connections[42];
// connect duplicate - shouldn't overwrite first one // connect duplicate - shouldn't overwrite first one
transport.OnServerConnected.Invoke(42); transport.OnServerConnected.Invoke(42, "");
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
Assert.That(NetworkServer.connections[42], Is.EqualTo(original)); Assert.That(NetworkServer.connections[42], Is.EqualTo(original));
} }
@ -230,13 +230,13 @@ public void AddConnection()
NetworkServer.Listen(1); NetworkServer.Listen(1);
// add first connection // add first connection
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42); NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42, "");
Assert.That(NetworkServer.AddConnection(conn42), Is.True); Assert.That(NetworkServer.AddConnection(conn42), Is.True);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42)); Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42));
// add second connection // add second connection
NetworkConnectionToClient conn43 = new NetworkConnectionToClient(43); NetworkConnectionToClient conn43 = new NetworkConnectionToClient(43, "");
Assert.That(NetworkServer.AddConnection(conn43), Is.True); Assert.That(NetworkServer.AddConnection(conn43), Is.True);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(2)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(2));
Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42)); Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42));
@ -250,13 +250,13 @@ public void AddConnection_PreventsDuplicates()
NetworkServer.Listen(1); NetworkServer.Listen(1);
// add a connection // add a connection
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42); NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42, "");
Assert.That(NetworkServer.AddConnection(conn42), Is.True); Assert.That(NetworkServer.AddConnection(conn42), Is.True);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42)); Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42));
// add duplicate connectionId // add duplicate connectionId
NetworkConnectionToClient connDup = new NetworkConnectionToClient(42); NetworkConnectionToClient connDup = new NetworkConnectionToClient(42, "");
Assert.That(NetworkServer.AddConnection(connDup), Is.False); Assert.That(NetworkServer.AddConnection(connDup), Is.False);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42)); Assert.That(NetworkServer.connections[42], Is.EqualTo(conn42));
@ -269,7 +269,7 @@ public void RemoveConnection()
NetworkServer.Listen(1); NetworkServer.Listen(1);
// add connection // add connection
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42); NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42, "");
Assert.That(NetworkServer.AddConnection(conn42), Is.True); Assert.That(NetworkServer.AddConnection(conn42), Is.True);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));
@ -285,7 +285,7 @@ public void DisconnectAllTest_RemoteConnection()
NetworkServer.Listen(1); NetworkServer.Listen(1);
// add connection // add connection
NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42); NetworkConnectionToClient conn42 = new NetworkConnectionToClient(42, "");
NetworkServer.AddConnection(conn42); NetworkServer.AddConnection(conn42);
Assert.That(NetworkServer.connections.Count, Is.EqualTo(1)); Assert.That(NetworkServer.connections.Count, Is.EqualTo(1));

View File

@ -298,7 +298,7 @@ public void TestClientExceptionCallback()
public void TestServerConnectedCallback(int id) public void TestServerConnectedCallback(int id)
{ {
int called = 0; int called = 0;
middleware.OnServerConnected = (i) => middleware.OnServerConnected = (i, addr) =>
{ {
called++; called++;
Assert.That(i, Is.EqualTo(id)); Assert.That(i, Is.EqualTo(id));
@ -306,10 +306,10 @@ public void TestServerConnectedCallback(int id)
// start to give callback to inner // start to give callback to inner
middleware.ServerStart(); middleware.ServerStart();
inner.OnServerConnected.Invoke(id); inner.OnServerConnected.Invoke(id, "");
Assert.That(called, Is.EqualTo(1)); Assert.That(called, Is.EqualTo(1));
inner.OnServerConnected.Invoke(id); inner.OnServerConnected.Invoke(id, "");
Assert.That(called, Is.EqualTo(2)); Assert.That(called, Is.EqualTo(2));
} }

View File

@ -238,7 +238,7 @@ public void TestServerConnected()
ArraySegment<byte> segment = new ArraySegment<byte>(data); ArraySegment<byte> segment = new ArraySegment<byte>(data);
// on connect, send a message back // on connect, send a message back
void SendMessage(int connectionId) void SendMessage(int connectionId, string address)
{ {
transport.ServerSend(connectionId, segment, 5); transport.ServerSend(connectionId, segment, 5);
} }
@ -247,7 +247,7 @@ void SendMessage(int connectionId)
transport.OnServerConnected = SendMessage; transport.OnServerConnected = SendMessage;
transport.ServerStart(); transport.ServerStart();
transport1.OnServerConnected.Invoke(1); transport1.OnServerConnected.Invoke(1, "");
transport1.Received().ServerSend(1, segment, 5); transport1.Received().ServerSend(1, segment, 5);
} }
@ -260,14 +260,14 @@ public void TestServerSend()
transport.ServerStart(); transport.ServerStart();
transport.ClientConnect("some.server.com"); transport.ClientConnect("some.server.com");
transport.OnServerConnected = _ => {}; transport.OnServerConnected = (connId, address) => {};
transport.OnServerDisconnected = _ => {}; transport.OnServerDisconnected = _ => {};
// connect two connectionIds. // connect two connectionIds.
// one of them very large to prevent // one of them very large to prevent
// https://github.com/vis2k/Mirror/issues/3280 // https://github.com/vis2k/Mirror/issues/3280
transport1.OnServerConnected(10); transport1.OnServerConnected(10, "");
transport2.OnServerConnected(int.MaxValue); transport2.OnServerConnected(int.MaxValue, "");
byte[] data = { 1, 2, 3 }; byte[] data = { 1, 2, 3 };
ArraySegment<byte> segment = new ArraySegment<byte>(data); ArraySegment<byte> segment = new ArraySegment<byte>(data);

View File

@ -95,7 +95,7 @@ public IEnumerator DisconnectTimeoutTest()
NetworkServer.disconnectInactiveTimeout = 1; NetworkServer.disconnectInactiveTimeout = 1;
GameObject remotePlayer = new GameObject("RemotePlayer", typeof(NetworkIdentity)); GameObject remotePlayer = new GameObject("RemotePlayer", typeof(NetworkIdentity));
NetworkConnectionToClient remoteConnection = new NetworkConnectionToClient(1); NetworkConnectionToClient remoteConnection = new NetworkConnectionToClient(1, "localhost");
NetworkServer.OnConnected(remoteConnection); NetworkServer.OnConnected(remoteConnection);
NetworkServer.AddPlayerForConnection(remoteConnection, remotePlayer); NetworkServer.AddPlayerForConnection(remoteConnection, remotePlayer);

View File

@ -28,7 +28,7 @@ public class EdgegapKcpServer : KcpServer
bool relayActive; bool relayActive;
public EdgegapKcpServer( public EdgegapKcpServer(
Action<int> OnConnected, Action<int, IPEndPoint> OnConnected,
Action<int, ArraySegment<byte>, KcpChannel> OnData, Action<int, ArraySegment<byte>, KcpChannel> OnData,
Action<int> OnDisconnected, Action<int> OnDisconnected,
Action<int, ErrorCode, string> OnError, Action<int, ErrorCode, string> OnError,

View File

@ -60,7 +60,7 @@ protected override void Awake()
// server // server
server = new EdgegapKcpServer( server = new EdgegapKcpServer(
(connectionId) => OnServerConnected.Invoke(connectionId), (connectionId, endPoint) => OnServerConnected.Invoke(connectionId, endPoint.PrettyAddress()),
(connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, FromKcpChannel(channel)), (connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, FromKcpChannel(channel)),
(connectionId) => OnServerDisconnected.Invoke(connectionId), (connectionId) => OnServerDisconnected.Invoke(connectionId),
(connectionId, error, reason) => OnServerError.Invoke(connectionId, ToTransportError(error), reason), (connectionId, error, reason) => OnServerError.Invoke(connectionId, ToTransportError(error), reason),

View File

@ -76,7 +76,7 @@ private void HandleInnerServerDataReceived(int connId, ArraySegment<byte> data,
} }
} }
private void HandleInnerServerConnected(int connId) private void HandleInnerServerConnected(int connId, string address)
{ {
Debug.Log($"[EncryptionTransport] New connection #{connId}"); Debug.Log($"[EncryptionTransport] New connection #{connId}");
EncryptedConnection ec = null; EncryptedConnection ec = null;
@ -89,7 +89,7 @@ private void HandleInnerServerConnected(int connId)
{ {
Debug.Log($"[EncryptionTransport] Connection #{connId} is ready"); Debug.Log($"[EncryptionTransport] Connection #{connId} is ready");
ServerRemoveFromPending(ec); ServerRemoveFromPending(ec);
OnServerConnected?.Invoke(connId); OnServerConnected?.Invoke(connId, address);
}, },
(type, msg) => (type, msg) =>
{ {

View File

@ -122,7 +122,7 @@ protected virtual void Awake()
// server // server
server = new KcpServer( server = new KcpServer(
(connectionId) => OnServerConnected.Invoke(connectionId), (connectionId, endPoint) => OnServerConnected.Invoke(connectionId, endPoint.PrettyAddress()),
(connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, FromKcpChannel(channel)), (connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, FromKcpChannel(channel)),
(connectionId) => OnServerDisconnected.Invoke(connectionId), (connectionId) => OnServerDisconnected.Invoke(connectionId),
(connectionId, error, reason) => OnServerError.Invoke(connectionId, ToTransportError(error), reason), (connectionId, error, reason) => OnServerError.Invoke(connectionId, ToTransportError(error), reason),

View File

@ -18,7 +18,7 @@ public class KcpServer
// events are readonly, set in constructor. // events are readonly, set in constructor.
// this ensures they are always initialized when used. // this ensures they are always initialized when used.
// fixes https://github.com/MirrorNetworking/Mirror/issues/3337 and more // fixes https://github.com/MirrorNetworking/Mirror/issues/3337 and more
protected readonly Action<int> OnConnected; protected readonly Action<int, IPEndPoint> OnConnected; // connectionId, address
protected readonly Action<int, ArraySegment<byte>, KcpChannel> OnData; protected readonly Action<int, ArraySegment<byte>, KcpChannel> OnData;
protected readonly Action<int> OnDisconnected; protected readonly Action<int> OnDisconnected;
protected readonly Action<int, ErrorCode, string> OnError; protected readonly Action<int, ErrorCode, string> OnError;
@ -43,7 +43,7 @@ public class KcpServer
public Dictionary<int, KcpServerConnection> connections = public Dictionary<int, KcpServerConnection> connections =
new Dictionary<int, KcpServerConnection>(); new Dictionary<int, KcpServerConnection>();
public KcpServer(Action<int> OnConnected, public KcpServer(Action<int, IPEndPoint> OnConnected,
Action<int, ArraySegment<byte>, KcpChannel> OnData, Action<int, ArraySegment<byte>, KcpChannel> OnData,
Action<int> OnDisconnected, Action<int> OnDisconnected,
Action<int, ErrorCode, string> OnError, Action<int, ErrorCode, string> OnError,
@ -184,6 +184,7 @@ public void Disconnect(int connectionId)
} }
// expose the whole IPEndPoint, not just the IP address. some need it. // expose the whole IPEndPoint, not just the IP address. some need it.
[Obsolete("KcpServer.GetClientEndPoint() isn't needed anymore. Connection endpoints are now passed in the KcpServer.OnConnected event in order to make threaded transports easier.")]
public IPEndPoint GetClientEndPoint(int connectionId) public IPEndPoint GetClientEndPoint(int connectionId)
{ {
if (connections.TryGetValue(connectionId, out KcpServerConnection connection)) if (connections.TryGetValue(connectionId, out KcpServerConnection connection))
@ -285,7 +286,8 @@ void OnConnectedCallback(KcpServerConnection conn)
// finally, call mirror OnConnected event // finally, call mirror OnConnected event
Log.Info($"[KCP] Server: OnConnected({connectionId})"); Log.Info($"[KCP] Server: OnConnected({connectionId})");
OnConnected(connectionId); IPEndPoint endPoint = conn.remoteEndPoint as IPEndPoint;
OnConnected(connectionId, endPoint);
} }
void OnDisconnectedCallback() void OnDisconnectedCallback()

View File

@ -261,12 +261,12 @@ void AddServerCallbacks()
int transportIndex = i; int transportIndex = i;
Transport transport = transports[i]; Transport transport = transports[i];
transport.OnServerConnected = (originalConnectionId => transport.OnServerConnected = (originalConnectionId, originalAddress) =>
{ {
// invoke Multiplex event with multiplexed connectionId // invoke Multiplex event with multiplexed connectionId
int multiplexedId = AddToLookup(originalConnectionId, transportIndex); int multiplexedId = AddToLookup(originalConnectionId, transportIndex);
OnServerConnected.Invoke(multiplexedId); OnServerConnected.Invoke(multiplexedId, originalAddress);
}); };
transport.OnServerDataReceived = (originalConnectionId, data, channel) => transport.OnServerDataReceived = (originalConnectionId, data, channel) =>
{ {
@ -282,7 +282,7 @@ void AddServerCallbacks()
} }
else else
Debug.LogWarning($"[Multiplexer] Received data for unknown connectionId={originalConnectionId} on transport={transportIndex}"); Debug.LogWarning($"[Multiplexer] Received data for unknown connectionId={originalConnectionId} on transport={transportIndex}");
return; return;
} }
OnServerDataReceived.Invoke(multiplexedId, data, channel); OnServerDataReceived.Invoke(multiplexedId, data, channel);
@ -302,7 +302,7 @@ void AddServerCallbacks()
} }
else else
Debug.LogError($"[Multiplexer] Received error for unknown connectionId={originalConnectionId} on transport={transportIndex}"); Debug.LogError($"[Multiplexer] Received error for unknown connectionId={originalConnectionId} on transport={transportIndex}");
return; return;
} }
OnServerError.Invoke(multiplexedId, error, reason); OnServerError.Invoke(multiplexedId, error, reason);
@ -329,7 +329,7 @@ void AddServerCallbacks()
} }
else else
Debug.LogWarning($"[Multiplexer] Received disconnect for unknown connectionId={originalConnectionId} on transport={transportIndex}"); Debug.LogWarning($"[Multiplexer] Received disconnect for unknown connectionId={originalConnectionId} on transport={transportIndex}");
return; return;
} }
OnServerDisconnected.Invoke(multiplexedId); OnServerDisconnected.Invoke(multiplexedId);

View File

@ -6,7 +6,7 @@ namespace Mirror.SimpleWeb
{ {
public class SimpleWebServer public class SimpleWebServer
{ {
public event Action<int> onConnect; public event Action<int, string> onConnect; // connectionId, address
public event Action<int> onDisconnect; public event Action<int> onDisconnect;
public event Action<int, ArraySegment<byte>> onData; public event Action<int, ArraySegment<byte>> onData;
public event Action<int, Exception> onError; public event Action<int, Exception> onError;
@ -91,7 +91,8 @@ public void ProcessMessageQueue(MonoBehaviour behaviour)
switch (next.type) switch (next.type)
{ {
case EventType.Connected: case EventType.Connected:
onConnect?.Invoke(next.connId); string address = GetClientAddress(next.connId);
onConnect?.Invoke(next.connId, address);
break; break;
case EventType.Data: case EventType.Data:
onData?.Invoke(next.connId, next.data.ToSegment()); onData?.Invoke(next.connId, next.data.ToSegment());

View File

@ -11,7 +11,7 @@ public class Server : Common
{ {
// events to hook into // events to hook into
// => OnData uses ArraySegment for allocation free receives later // => OnData uses ArraySegment for allocation free receives later
public Action<int> OnConnected; public Action<int, IPEndPoint> OnConnected; // connectionId, IPEndPoint
public Action<int, ArraySegment<byte>> OnData; public Action<int, ArraySegment<byte>> OnData;
public Action<int> OnDisconnected; public Action<int> OnDisconnected;
@ -316,16 +316,16 @@ public bool Send(int connectionId, ArraySegment<byte> message)
} }
// client's ip is sometimes needed by the server, e.g. for bans // client's ip is sometimes needed by the server, e.g. for bans
public string GetClientAddress(int connectionId) IPEndPoint GetClientEndPoint(int connectionId)
{ {
try try
{ {
// find the connection // find the connection
if (clients.TryGetValue(connectionId, out ConnectionState connection)) if (clients.TryGetValue(connectionId, out ConnectionState connection))
{ {
return ((IPEndPoint)connection.client.Client.RemoteEndPoint).Address.ToString(); return (IPEndPoint)connection.client.Client.RemoteEndPoint;
} }
return ""; return null;
} }
catch (SocketException) catch (SocketException)
{ {
@ -337,10 +337,17 @@ public string GetClientAddress(int connectionId)
// incompatible with the requested protocol was used at // incompatible with the requested protocol was used at
// System.Net.Sockets.Socket.get_LocalEndPoint () // System.Net.Sockets.Socket.get_LocalEndPoint ()
// so let's at least catch it and recover // so let's at least catch it and recover
return "unknown"; return null;
} }
} }
[Obsolete("Telepathy.Server.GetClientEndPoint() isn't needed anymore. Connection endpoints are now passed in the Telepathy.Server.OnConnected event in order to make threaded transports easier.")]
public string GetClientAddress(int connectionId)
{
IPEndPoint endPoint = GetClientEndPoint(connectionId);
return endPoint != null ? endPoint.Address.ToString() : "unknown";
}
// disconnect (kick) a client // disconnect (kick) a client
public bool Disconnect(int connectionId) public bool Disconnect(int connectionId)
{ {
@ -388,7 +395,9 @@ public int Tick(int processLimit, Func<bool> checkEnabled = null)
switch (eventType) switch (eventType)
{ {
case EventType.Connected: case EventType.Connected:
OnConnected?.Invoke(connectionId); // pass address in OnConnected for easier ThreadedTransport support
IPEndPoint endPoint = GetClientEndPoint(connectionId);
OnConnected?.Invoke(connectionId, endPoint);
break; break;
case EventType.Data: case EventType.Data:
OnData?.Invoke(connectionId, message); OnData?.Invoke(connectionId, message);

View File

@ -178,7 +178,7 @@ public override void ServerStart()
// system's hook (e.g. statistics OnData) was added is to wrap // system's hook (e.g. statistics OnData) was added is to wrap
// them all in a lambda and always call the latest hook. // them all in a lambda and always call the latest hook.
// (= lazy call) // (= lazy call)
server.OnConnected = (connectionId) => OnServerConnected.Invoke(connectionId); server.OnConnected = (connectionId, endPoint) => OnServerConnected.Invoke(connectionId, endPoint.PrettyAddress());
server.OnData = (connectionId, segment) => OnServerDataReceived.Invoke(connectionId, segment, Channels.Reliable); server.OnData = (connectionId, segment) => OnServerDataReceived.Invoke(connectionId, segment, Channels.Reliable);
server.OnDisconnected = (connectionId) => OnServerDisconnected.Invoke(connectionId); server.OnDisconnected = (connectionId) => OnServerDisconnected.Invoke(connectionId);

View File

@ -5,6 +5,7 @@
// note that ThreadLog.cs is required for Debug.Log from threads to work in builds. // note that ThreadLog.cs is required for Debug.Log from threads to work in builds.
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using UnityEngine; using UnityEngine;
@ -317,9 +318,11 @@ protected void OnThreadedClientDisconnected()
EnqueueClientMain(ClientMainEventType.OnClientDisconnected, null, null, null); EnqueueClientMain(ClientMainEventType.OnClientDisconnected, null, null, null);
} }
protected void OnThreadedServerConnected(int connectionId) protected void OnThreadedServerConnected(int connectionId, IPEndPoint endPoint)
{ {
EnqueueServerMain(ServerMainEventType.OnServerConnected, null, connectionId, null, null); // create string copy of address immediately before sending to another thread
string address = endPoint.PrettyAddress();
EnqueueServerMain(ServerMainEventType.OnServerConnected, address, connectionId, null, null);
} }
protected void OnThreadedServerSend(int connectionId, ArraySegment<byte> message, int channelId) protected void OnThreadedServerSend(int connectionId, ArraySegment<byte> message, int channelId)
@ -515,9 +518,9 @@ public override void ServerEarlyUpdate()
// SERVER EVENTS /////////////////////////////////////////// // SERVER EVENTS ///////////////////////////////////////////
case ServerMainEventType.OnServerConnected: case ServerMainEventType.OnServerConnected:
{ {
// call original transport event // call original transport event with connectionId, address
// TODO pass client address in OnConnect here later string address = (string)elem.param;
OnServerConnected?.Invoke(elem.connectionId.Value);//, (string)elem.param); OnServerConnected?.Invoke(elem.connectionId.Value, address);
break; break;
} }
case ServerMainEventType.OnServerSent: case ServerMainEventType.OnServerSent:
@ -612,7 +615,7 @@ public override void ServerDisconnect(int connectionId)
// querying this at runtime won't work for threaded transports. // querying this at runtime won't work for threaded transports.
public override string ServerGetClientAddress(int connectionId) public override string ServerGetClientAddress(int connectionId)
{ {
throw new NotImplementedException(); throw new NotImplementedException("ThreadedTransport passes each connection's address in OnServerConnected. Don't use ServerGetClientAddress.");
} }
public override void ServerStop() public override void ServerStop()