IPv6 & multi-peer NetManager support. (#705)

1. IPv6 support. Woo!
2. NetManager can now explicitly bind to adresses and work with multiple peers. ~~I thought I needed this for IPv6 because I forgot about IPv4-mapped adresses, but the code's written so let's keep it.~~ OpenBSD doesn't support IPv4-mapped IPv6 addresses so effort not wasted.
This commit is contained in:
Pieter-Jan Briers
2018-11-21 20:57:47 +01:00
committed by GitHub
parent 1fc18c3538
commit 153b57ed56
10 changed files with 357 additions and 214 deletions

View File

@@ -114,7 +114,13 @@ namespace Lidgren.Network
iep = new IPEndPoint(m_configuration.LocalAddress, m_configuration.Port);
EndPoint ep = (EndPoint)iep;
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
m_socket = new Socket(ep.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
if (ep.AddressFamily == AddressFamily.InterNetworkV6)
{
// Disable IPv4 -> IPv6 mapping.
// SS14 handles IPv6 & IPv4 concurrently with a different net peer.
m_socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, true);
}
m_socket.ReceiveBufferSize = m_configuration.ReceiveBufferSize;
m_socket.SendBufferSize = m_configuration.SendBufferSize;
m_socket.Blocking = false;
@@ -386,6 +392,7 @@ namespace Lidgren.Network
int bytesReceived = 0;
try
{
var honk = this;
bytesReceived = m_socket.ReceiveFrom(m_receiveBuffer, 0, m_receiveBuffer.Length, SocketFlags.None, ref m_senderRemote);
}
catch (SocketException sx)

View File

@@ -2,6 +2,7 @@ using System;
using System.Threading;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
namespace Lidgren.Network
{
@@ -102,7 +103,14 @@ namespace Lidgren.Network
m_connections = new List<NetConnection>();
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>();
m_handshakes = new Dictionary<IPEndPoint, NetConnection>();
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
if (m_configuration.LocalAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.IPv6Any, 0);
}
else
{
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
}
m_status = NetPeerStatus.NotRunning;
m_receivedFragmentGroups = new Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>>();
}

View File

@@ -85,7 +85,8 @@ namespace Lidgren.Network
IPAddress ipAddress = null;
if (IPAddress.TryParse(ipOrHost, out ipAddress))
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
if (ipAddress.AddressFamily == AddressFamily.InterNetwork
|| ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
callback(ipAddress);
return;
@@ -123,16 +124,23 @@ namespace Lidgren.Network
}
// check each entry for a valid IP address
IPAddress bestAddress = null;
foreach (IPAddress ipCurrent in entry.AddressList)
{
if (ipCurrent.AddressFamily == AddressFamily.InterNetwork)
// Prefer IPv6 addresses.
if (ipCurrent.AddressFamily == AddressFamily.InterNetworkV6)
{
callback(ipCurrent);
return;
}
if (ipCurrent.AddressFamily == AddressFamily.InterNetwork)
{
bestAddress = ipCurrent;
}
}
callback(null);
callback(bestAddress);
}, null);
}
catch (SocketException ex)
@@ -161,7 +169,8 @@ namespace Lidgren.Network
IPAddress ipAddress = null;
if (IPAddress.TryParse(ipOrHost, out ipAddress))
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
if (ipAddress.AddressFamily == AddressFamily.InterNetwork
|| ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
return ipAddress;
throw new ArgumentException("This method will not currently resolve other than ipv4 addresses");
}
@@ -175,13 +184,22 @@ namespace Lidgren.Network
return null;
// check each entry for a valid IP address
IPAddress bestAddress = null;
foreach (IPAddress ipCurrent in entry.AddressList)
{
if (ipCurrent.AddressFamily == AddressFamily.InterNetwork)
if (ipCurrent.AddressFamily == AddressFamily.InterNetworkV6)
{
return ipCurrent;
}
// Prefer IPv6 addresses.
if (ipCurrent.AddressFamily == AddressFamily.InterNetwork)
{
bestAddress = ipCurrent;
}
}
return null;
return bestAddress;
}
catch (SocketException ex)
{
@@ -391,6 +409,11 @@ namespace Lidgren.Network
/// </summary>
public static bool IsLocal(IPAddress remote)
{
if (remote.AddressFamily == AddressFamily.InterNetworkV6)
{
// TODO: Can this be made to work? Do we even care for SS14?
return false;
}
IPAddress mask;
IPAddress local = GetMyAddress(out mask);

View File

@@ -65,11 +65,14 @@ namespace SS14.Client
/// <inheritdoc />
public void ConnectToServer(string ip, ushort port)
{
if (RunLevel == ClientRunLevel.Connecting)
{
_net.Shutdown("Client mashing that connect button.");
Reset();
}
DebugTools.Assert(RunLevel < ClientRunLevel.Connecting);
DebugTools.Assert(!_net.IsConnected);
_net.Startup();
OnRunLevelChanged(ClientRunLevel.Connecting);
_net.ClientConnect(ip, port, PlayerNameOverride ?? _configManager.GetCVar<string>("player.name"));
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text.RegularExpressions;
using SS14.Client.Input;
using SS14.Client.Interfaces;
using SS14.Client.Interfaces.UserInterface;
@@ -32,6 +33,9 @@ namespace SS14.Client.State.States
private OptionsMenu OptionsMenu;
// ReSharper disable once InconsistentNaming
private static readonly Regex IPv6Regex = new Regex(@"\[(.*:.*:.*)](?::(\d+))?");
/// <inheritdoc />
public override void Startup()
{
@@ -96,7 +100,8 @@ namespace SS14.Client.State.States
}
catch (ArgumentException e)
{
userInterfaceManager.Popup($"Unable to resolve address: {e.Message}", "Invalid IP");
userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error.");
Logger.Warning(e.ToString());
}
}
@@ -110,6 +115,21 @@ namespace SS14.Client.State.States
private void ParseAddress(string address, out string ip, out ushort port)
{
var match6 = IPv6Regex.Match(address);
if (match6 != Match.Empty)
{
ip = match6.Groups[1].Value;
if (!match6.Groups[2].Success)
{
port = _client.DefaultPort;
}
else if (!ushort.TryParse(match6.Groups[2].Value, out port))
{
throw new ArgumentException("Not a valid port.");
}
return;
}
// See if the IP includes a port.
var split = address.Split(':');
ip = address;

View File

@@ -169,18 +169,13 @@ namespace SS14.Server
try
{
netMan.Initialize(true);
netMan.Startup();
}
catch (System.Net.Sockets.SocketException)
{
var port = netMan.Port;
Logger.Fatal($"Unable to setup networking manager. Check port {port} is not already in use!, shutting down...");
Environment.Exit(1);
netMan.StartServer();
}
catch (Exception e)
{
Logger.Fatal($"Unable to setup networking manager. Unknown exception: {e}, shutting down...");
Environment.Exit(1);
var port = netMan.Port;
Logger.Fatal("Unable to setup networking manager. Check port {0} is not already in use and that all binding addresses are correct!\n{1}", port, e);
return true;
}
// Set up the VFS

View File

@@ -64,17 +64,17 @@ namespace SS14.Server.Console.Commands
var sb = new StringBuilder();
var players = IoCManager.Resolve<IPlayerManager>().GetAllPlayers();
sb.AppendLine($"{"Player Name",20}{"IP Address",16}{"Status",12}{"Playing Time",14}{"Ping",9}");
sb.AppendLine("-----------------------------------------------------------------");
sb.AppendLine($"{"Player Name",20} {"Status",12} {"Playing Time",14} {"Ping",9} {"IP EndPoint",20}");
sb.AppendLine("-------------------------------------------------------------------------------");
foreach (IPlayerSession p in players)
foreach (var p in players)
{
sb.Append($" {p.Name,20}");
sb.AppendLine(string.Format(" {0,21}{1,12}{2,14}{3,9}",
sb.AppendLine(string.Format("{4,20} {1,12} {2,14:hh\\:mm\\:ss} {3,9} {0,20}",
p.ConnectedClient.RemoteEndPoint,
p.Status.ToString(),
(DateTime.Now - p.ConnectedTime).ToString(@"hh\:mm\:ss"),
p.ConnectedClient.Ping + "ms"));
DateTime.Now - p.ConnectedTime,
p.ConnectedClient.Ping + "ms",
p.Name));
}
shell.SendText(player, sb.ToString());

View File

@@ -8,6 +8,7 @@ enabled = false
[net]
tickrate = 60
port = 1212
bindto = "::,0.0.0.0"
[game]
hostname = "MyServer"

View File

@@ -58,7 +58,7 @@ namespace SS14.Shared.Interfaces.Network
/// <summary>
/// Starts the server running and listening on the port.
/// </summary>
void Startup();
void StartServer();
/// <summary>
/// Shuts down this peer, disconnecting all channels.
@@ -66,12 +66,6 @@ namespace SS14.Shared.Interfaces.Network
/// <param name="reason">String describing why the peer was shut down.</param>
void Shutdown(string reason);
/// <summary>
/// Restarts this peer, disconnecting all channels.
/// </summary>
/// <param name="reason">String describing why the peer was restarted.</param>
void Restart(string reason);
/// <summary>
/// Process all queued packets. Should be called often.
/// </summary>

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Lidgren.Network;
using SS14.Shared.Configuration;
@@ -37,10 +38,10 @@ namespace SS14.Shared.Network
/// </summary>
private readonly Dictionary<NetConnection, NetChannel> _channels = new Dictionary<NetConnection, NetChannel>();
private readonly Dictionary<NetConnection, NetSessionId> _assignedSessions = new Dictionary<NetConnection, NetSessionId>();
private readonly Dictionary<NetConnection, NetSessionId> _assignedSessions =
new Dictionary<NetConnection, NetSessionId>();
[Dependency]
private readonly IConfigurationManager _config;
[Dependency] private readonly IConfigurationManager _config;
/// <summary>
/// Holds lookup table for NetMessage.Id -> NetMessage.Type
@@ -53,9 +54,9 @@ namespace SS14.Shared.Network
private readonly StringTable _strings = new StringTable();
/// <summary>
/// The instance of the net server.
/// The list of network peers we are listening on.
/// </summary>
private NetPeer _netPeer;
private readonly List<NetPeer> _netPeers = new List<NetPeer>();
/// <inheritdoc />
public int Port => _config.GetCVar<int>("net.port");
@@ -67,11 +68,31 @@ namespace SS14.Shared.Network
public bool IsClient => !IsServer;
/// <inheritdoc />
public bool IsConnected => _netPeer != null && _netPeer.ConnectionsCount > 0;
public bool IsConnected => _netPeers.Any(p => p.ConnectionsCount > 0);
public bool IsRunning => _netPeer != null;
public bool IsRunning => _netPeers.Count != 0;
public NetworkStats Statistics => new NetworkStats(_netPeer.Statistics);
public NetworkStats Statistics
{
get
{
var sentPackets = 0;
var sentBytes = 0;
var recvPackets = 0;
var recvBytes = 0;
foreach (var peer in _netPeers)
{
var netPeerStatistics = peer.Statistics;
sentPackets += netPeerStatistics.SentPackets;
sentBytes += netPeerStatistics.SentBytes;
recvPackets += netPeerStatistics.ReceivedPackets;
recvBytes += netPeerStatistics.ReceivedBytes;
}
return new NetworkStats(sentBytes, recvBytes, sentPackets, recvPackets);
}
}
/// <inheritdoc />
public IEnumerable<INetChannel> Channels => _channels.Values;
@@ -81,11 +102,37 @@ namespace SS14.Shared.Network
public IReadOnlyDictionary<Type, ProcessMessage> CallbackAudit => _callbacks;
/// <inheritdoc />
public INetChannel ServerChannel
{
get
{
DebugTools.Assert(IsClient);
if (_netPeers.Count == 0)
{
return null;
}
var peer = _netPeers[0];
if (peer.ConnectionsCount == 0)
{
return null;
}
return GetChannel(peer.Connections[0]);
}
}
private bool _initialized;
/// <inheritdoc />
public void Initialize(bool isServer)
{
if (_netPeer != null)
throw new InvalidOperationException("[NET] NetManager has already been initialized.");
if (_initialized)
{
throw new InvalidOperationException("NetManager has already been initialized.");
}
IsServer = isServer;
@@ -99,6 +146,11 @@ namespace SS14.Shared.Network
_config.RegisterCVar("net.interpolation", 0.1f, CVar.ARCHIVE);
_config.RegisterCVar("net.rate", 10240, CVar.REPLICATED | CVar.ARCHIVE);
}
else
{
// That's comma-separated, btw.
_config.RegisterCVar("net.bindto", "0.0.0.0,::", CVar.ARCHIVE);
}
#if DEBUG
_config.RegisterCVar("net.fakelag", false, CVar.CHEAT);
@@ -107,22 +159,166 @@ namespace SS14.Shared.Network
_config.RegisterCVar("net.fakelagrand", 0.0f, CVar.CHEAT);
#endif
_strings.Initialize(this, () =>
{
OnConnected(ServerChannel);
});
_strings.Initialize(this, () => { OnConnected(ServerChannel); });
_initialized = true;
}
public void Startup()
public void StartServer()
{
DebugTools.Assert(IsServer);
DebugTools.Assert(!IsRunning);
var binds = _config.GetCVar<string>("net.bindto").Split(',');
foreach (var bindAddress in binds)
{
if (!IPAddress.TryParse(bindAddress.Trim(), out var address))
{
throw new InvalidOperationException("Not a valid IPv4 or IPv6 address");
}
var config = _getBaseNetPeerConfig();
config.LocalAddress = address;
config.Port = Port;
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
var peer = new NetPeer(config);
peer.Start();
_netPeers.Add(peer);
}
if (_netPeers.Count == 0)
{
Logger.WarningS("net",
"Exactly 0 addresses have been bound to, nothing will be able to connect to the server.");
}
}
public void Dispose()
{
Shutdown("Network manager getting disposed.");
}
/// <inheritdoc />
public void Shutdown(string reason)
{
foreach (var kvChannel in _channels)
DisconnectChannel(kvChannel.Value, reason);
// request shutdown of the netPeer
_netPeers.ForEach(p => p.Shutdown(reason));
_netPeers.Clear();
// wait for the network thread to finish its work (like flushing packets and gracefully disconnecting)
// Lidgren does not expose the thread, so we can't join or or anything
// pretty much have to poll every so often and wait for it to finish before continuing
// when the network thread is finished, it will change status from ShutdownRequested to NotRunning
while (_netPeers.Any(p => p.Status == NetPeerStatus.ShutdownRequested))
{
// sleep the thread for an arbitrary length so it isn't spinning in the while loop as much
Thread.Sleep(50);
}
_strings.Reset();
}
public void ProcessPackets()
{
foreach (var peer in _netPeers)
{
NetIncomingMessage msg;
while ((msg = peer.ReadMessage()) != null)
{
switch (msg.MessageType)
{
case NetIncomingMessageType.VerboseDebugMessage:
Logger.DebugS("net", "{0}: {1}", peer.Configuration.LocalAddress, msg.ReadString());
break;
case NetIncomingMessageType.DebugMessage:
Logger.InfoS("net", "{0}: {1}", peer.Configuration.LocalAddress, msg.ReadString());
break;
case NetIncomingMessageType.WarningMessage:
Logger.WarningS("net", "{0}: {1}", peer.Configuration.LocalAddress, msg.ReadString());
break;
case NetIncomingMessageType.ErrorMessage:
Logger.ErrorS("net", "{0}: {1}", peer.Configuration.LocalAddress, msg.ReadString());
break;
case NetIncomingMessageType.ConnectionApproval:
HandleApproval(msg);
break;
case NetIncomingMessageType.Data:
DispatchNetMessage(msg);
break;
case NetIncomingMessageType.StatusChanged:
HandleStatusChanged(msg);
break;
default:
Logger.WarningS("net",
"{0}: Unhandled incoming packet type from {1}: {2}",
peer.Configuration.LocalAddress,
msg.SenderConnection.RemoteEndPoint,
msg.MessageType);
break;
}
peer.Recycle(msg);
}
}
}
/// <inheritdoc />
public void ClientConnect(string host, int port, string userNameRequest)
{
DebugTools.Assert(!IsServer, "Should never be called on the server.");
DebugTools.Assert(!IsConnected);
if (IsRunning)
{
ClientDisconnect("Client left server.");
}
// Set up NetPeer.
var endPoint = NetUtility.Resolve(host, port);
Logger.InfoS("net", $"Connecting to {endPoint}...");
var config = _getBaseNetPeerConfig();
if (endPoint.AddressFamily == AddressFamily.InterNetworkV6)
{
config.LocalAddress = IPAddress.IPv6Any;
}
else
{
config.LocalAddress = IPAddress.Any;
}
var peer = new NetPeer(config);
peer.Start();
_netPeers.Add(peer);
var hail = peer.CreateMessage();
hail.Write(userNameRequest);
peer.Connect(host, port, hail);
}
/// <inheritdoc />
public void ClientDisconnect(string reason)
{
DebugTools.Assert(IsClient, "Should never be called on the server.");
Shutdown(reason);
}
private NetPeerConfiguration _getBaseNetPeerConfig()
{
var netConfig = new NetPeerConfiguration("SS14_NetTag");
if (IsServer)
{
netConfig.Port = Port;
netConfig.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
}
#if DEBUG
//Simulate Latency
if (_config.GetCVar<bool>("net.fakelag"))
@@ -134,140 +330,9 @@ namespace SS14.Shared.Network
netConfig.ConnectionTimeout = 30000f;
#endif
_netPeer = new NetPeer(netConfig);
_netPeer.Start();
return netConfig;
}
public void Dispose()
{
if (_netPeer != null)
{
Shutdown("Network manager getting disposed.");
}
}
/// <inheritdoc />
public void Shutdown(string reason)
{
foreach (var kvChannel in _channels)
DisconnectChannel(kvChannel.Value, reason);
// request shutdown of the netPeer
_netPeer.Shutdown(reason);
// wait for the network thread to finish its work (like flushing packets and gracefully disconnecting)
// Lidgren does not expose the thread, so we can't join or or anything
// pretty much have to poll every so often and wait for it to finish before continuing
// when the network thread is finished, it will change status from ShutdownRequested to NotRunning
while (_netPeer.Status == NetPeerStatus.ShutdownRequested)
{
// sleep the thread for an arbitrary length so it isn't spinning in the while loop as much
Thread.Sleep(50);
}
_strings.Reset();
}
/// <inheritdoc />
public void Restart(string reason)
{
Shutdown(reason);
Startup();
}
/// <summary>
/// Process incoming packets.
/// </summary>
public void ProcessPackets()
{
// client does not always have its networking running, for example on main menu
if (IsClient && _netPeer == null)
return;
// server on the other hand needs it to be running
DebugTools.Assert(_netPeer != null);
NetIncomingMessage msg;
while ((msg = _netPeer.ReadMessage()) != null)
{
switch (msg.MessageType)
{
case NetIncomingMessageType.VerboseDebugMessage:
Logger.DebugS("net", msg.ReadString());
break;
case NetIncomingMessageType.DebugMessage:
Logger.InfoS("net", msg.ReadString());
break;
case NetIncomingMessageType.WarningMessage:
Logger.WarningS("net", msg.ReadString());
break;
case NetIncomingMessageType.ErrorMessage:
Logger.ErrorS("net", msg.ReadString());
break;
case NetIncomingMessageType.ConnectionApproval:
HandleApproval(msg);
break;
case NetIncomingMessageType.Data:
DispatchNetMessage(msg);
break;
case NetIncomingMessageType.StatusChanged:
HandleStatusChanged(msg);
break;
default:
Logger.WarningS("net", "{msg.SenderConnection.RemoteEndPoint.Address}: Unhandled incoming packet type: {msg.MessageType}");
break;
}
_netPeer.Recycle(msg);
}
}
/// <inheritdoc />
public void ClientConnect(string host, int port, string userNameRequest)
{
DebugTools.Assert(_netPeer != null);
DebugTools.Assert(!IsServer, "Should never be called on the server.");
DebugTools.Assert(!IsConnected);
if (_netPeer.ConnectionsCount > 0)
ClientDisconnect("Client left server.");
Logger.InfoS("net", $"Connecting to {host}:{port}...");
var hail = _netPeer.CreateMessage();
hail.Write(userNameRequest);
_netPeer.Connect(host, port, hail);
}
/// <inheritdoc />
public void ClientDisconnect(string reason)
{
DebugTools.Assert(IsClient, "Should never be called on the server.");
if (_netPeer == null)
return;
// Client should never have more than one connection.
DebugTools.Assert(_netPeer.ConnectionsCount <= 1);
foreach (var connection in _netPeer.Connections)
{
connection.Disconnect(reason);
}
Shutdown(reason);
}
/// <inheritdoc />
public INetChannel ServerChannel => _netPeer.ConnectionsCount > 0 ? GetChannel(_netPeer.Connections[0]) : null;
/// <summary>
/// Gets the NetChannel of a peer NetConnection.
/// </summary>
@@ -298,11 +363,13 @@ namespace SS14.Shared.Network
case NetConnectionStatus.Disconnected:
if (_channels.ContainsKey(sender))
HandleDisconnect(msg);
else if (sender.RemoteUniqueIdentifier == 0) // is this the best way to detect an unsuccessful connect?
else if (sender.RemoteUniqueIdentifier == 0
) // is this the best way to detect an unsuccessful connect?
{
Logger.InfoS("net", $"{sender.RemoteEndPoint}: Failed to connect");
OnConnectFailed();
}
break;
}
}
@@ -331,7 +398,7 @@ namespace SS14.Shared.Network
if (OnConnecting(ip, session))
{
_assignedSessions.Add(sender, session);
var msg = _netPeer.CreateMessage();
var msg = message.SenderConnection.Peer.CreateMessage();
msg.Write(name);
sender.Approve(msg);
}
@@ -352,6 +419,7 @@ namespace SS14.Shared.Network
{
session = _assignedSessions[sender];
}
var channel = new NetChannel(this, sender, session);
_channels.Add(sender, channel);
@@ -398,10 +466,11 @@ namespace SS14.Shared.Network
private void DispatchNetMessage(NetIncomingMessage msg)
{
if (_netPeer.Status == NetPeerStatus.ShutdownRequested)
var peer = msg.SenderConnection.Peer;
if (peer.Status == NetPeerStatus.ShutdownRequested)
return;
if (_netPeer.Status == NetPeerStatus.NotRunning)
if (peer.Status == NetPeerStatus.NotRunning)
return;
if (!IsConnected)
@@ -428,7 +497,7 @@ namespace SS14.Shared.Network
}
var channel = GetChannel(msg.SenderConnection);
var instance = (NetMessage)Activator.CreateInstance(packetType, channel);
var instance = (NetMessage) Activator.CreateInstance(packetType, channel);
instance.MsgChannel = channel;
try
@@ -437,12 +506,14 @@ namespace SS14.Shared.Network
}
catch (Exception e) // yes, we want to catch ALL exeptions for security
{
Logger.WarningS("net", $"{msg.SenderConnection.RemoteEndPoint}: Failed to deserialize {packetType.Name} packet: {e.Message}");
Logger.WarningS("net",
$"{msg.SenderConnection.RemoteEndPoint}: Failed to deserialize {packetType.Name} packet: {e.Message}");
}
if (!_callbacks.TryGetValue(packetType, out ProcessMessage callback))
{
Logger.WarningS("net", $"{msg.SenderConnection.RemoteEndPoint}: Received packet {id}:{name}, but callback was not registered.");
Logger.WarningS("net",
$"{msg.SenderConnection.RemoteEndPoint}: Received packet {id}:{name}, but callback was not registered.");
return;
}
@@ -460,24 +531,25 @@ namespace SS14.Shared.Network
_messages.Add(name, typeof(T));
if (rxCallback != null)
_callbacks.Add(typeof(T), msg => rxCallback((T)msg));
_callbacks.Add(typeof(T), msg => rxCallback((T) msg));
}
/// <inheritdoc />
public T CreateNetMessage<T>()
where T : NetMessage
{
return (T)Activator.CreateInstance(typeof(T), (INetChannel)null);
return (T) Activator.CreateInstance(typeof(T), (INetChannel) null);
}
private NetOutgoingMessage BuildMessage(NetMessage message)
private NetOutgoingMessage BuildMessage(NetMessage message, NetPeer peer)
{
var packet = _netPeer.CreateMessage(4);
var packet = peer.CreateMessage(4);
if (!_strings.TryFindStringId(message.MsgName, out int msgId))
throw new NetManagerException($"[NET] No string in table with name {message.MsgName}. Was it registered?");
throw new NetManagerException(
$"[NET] No string in table with name {message.MsgName}. Was it registered?");
packet.Write((byte)msgId);
packet.Write((byte) msgId);
message.WriteToBuffer(packet);
return packet;
}
@@ -485,31 +557,40 @@ namespace SS14.Shared.Network
/// <inheritdoc />
public void ServerSendToAll(NetMessage message)
{
if (_netPeer == null || _netPeer.ConnectionsCount == 0)
DebugTools.Assert(IsServer);
if (!IsConnected)
return;
var packet = BuildMessage(message);
var method = GetMethod(message.MsgGroup);
_netPeer.SendMessage(packet, _netPeer.Connections, method, 0);
foreach (var peer in _netPeers)
{
var packet = BuildMessage(message, peer);
var method = GetMethod(message.MsgGroup);
if (peer.ConnectionsCount == 0)
{
continue;
}
peer.SendMessage(packet, peer.Connections, method, 0);
}
}
/// <inheritdoc />
public void ServerSendMessage(NetMessage message, INetChannel recipient)
{
if (_netPeer == null)
return;
DebugTools.Assert(IsServer);
if (!(recipient is NetChannel channel))
throw new ArgumentException($"Not of type {typeof(NetChannel).FullName}", nameof(recipient));
var packet = BuildMessage(message);
_netPeer.SendMessage(packet, channel.Connection, NetDeliveryMethod.ReliableOrdered);
var peer = channel.Connection.Peer;
var packet = BuildMessage(message, peer);
peer.SendMessage(packet, channel.Connection, NetDeliveryMethod.ReliableOrdered);
}
/// <inheritdoc />
public void ServerSendToMany(NetMessage message, List<INetChannel> recipients)
{
if (_netPeer == null)
DebugTools.Assert(IsServer);
if (!IsConnected)
return;
foreach (var channel in recipients)
@@ -521,16 +602,19 @@ namespace SS14.Shared.Network
/// <inheritdoc />
public void ClientSendMessage(NetMessage message)
{
if (_netPeer == null)
return;
DebugTools.Assert(IsClient);
// not connected to a server, so a message cannot be sent to it.
if (!IsConnected)
return;
var packet = BuildMessage(message);
DebugTools.Assert(_netPeers.Count == 1);
DebugTools.Assert(_netPeers[0].ConnectionsCount == 1);
var peer = _netPeers[0];
var packet = BuildMessage(message, peer);
var method = GetMethod(message.MsgGroup);
_netPeer.SendMessage(packet, _netPeer.Connections[0], method);
peer.SendMessage(packet, peer.Connections[0], method);
}
#endregion NetMessages
@@ -626,6 +710,14 @@ namespace SS14.Shared.Network
/// </summary>
public readonly int ReceivedPackets;
public NetworkStats(int sentBytes, int receivedBytes, int sentPackets, int receivedPackets)
{
SentBytes = sentBytes;
ReceivedBytes = receivedBytes;
SentPackets = sentPackets;
ReceivedPackets = receivedPackets;
}
/// <summary>
/// Creates an instance of this object.
/// </summary>