mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
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:
committed by
GitHub
parent
1fc18c3538
commit
153b57ed56
@@ -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)
|
||||
|
||||
@@ -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>>();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -8,6 +8,7 @@ enabled = false
|
||||
[net]
|
||||
tickrate = 60
|
||||
port = 1212
|
||||
bindto = "::,0.0.0.0"
|
||||
|
||||
[game]
|
||||
hostname = "MyServer"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user