NetManager connection handshake improvement. (#773)

* Reworked the net manager connection sequence.
* Correctly handle IPv4/IPv6 prioritization and fallback.
* Making a proper handshake for an auth system further down the line should be much easier now too.
* Added feedback to connection sequence in the main menu.
This commit is contained in:
Pieter-Jan Briers
2019-03-31 00:51:10 +01:00
committed by GitHub
parent b5635201c2
commit 25a485aea7
8 changed files with 516 additions and 87 deletions

View File

@@ -27,6 +27,7 @@ using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Lidgren.Network
{
@@ -156,6 +157,34 @@ namespace Lidgren.Network
}
}
public static async Task<IPAddress[]> ResolveAsync(string ipOrHost)
{
if (string.IsNullOrEmpty(ipOrHost))
throw new ArgumentException("Supplied string must not be empty", "ipOrHost");
ipOrHost = ipOrHost.Trim();
if (IPAddress.TryParse(ipOrHost, out var ipAddress))
{
if (ipAddress.AddressFamily == AddressFamily.InterNetwork
|| ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
return new[] {ipAddress};
}
throw new ArgumentException("This method will not currently resolve other than IPv4 or IPv6 addresses");
}
try
{
var entry = await Task.Factory.FromAsync(Dns.BeginGetHostEntry, Dns.EndGetHostEntry, ipOrHost, null);
return entry.AddressList;
}
catch (SocketException ex)
{
return null;
}
}
/// <summary>
/// Get IPv4 address from notation (xxx.xxx.xxx.xxx) or hostname
/// </summary>

View File

@@ -100,7 +100,7 @@ mouse_default_cursor_shape = 1
size_flags_horizontal = 1
size_flags_vertical = 1
custom_fonts/font = SubResource( 1 )
text = "127.0.0.1"
text = "localhost"
focus_mode = 2
context_menu_enabled = false
placeholder_text = "ip Address"

View File

@@ -15,6 +15,8 @@ using SS14.Client.Interfaces.State;
using SS14.Client.ResourceManagement;
using SS14.Client.UserInterface.Controls;
using SS14.Client.UserInterface.CustomControls;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.Network;
using SS14.Shared.Utility;
namespace SS14.Client.State.States
@@ -25,16 +27,15 @@ namespace SS14.Client.State.States
// Instantiated dynamically through the StateManager.
public class MainScreen : State
{
[Dependency]
readonly IBaseClient _client;
[Dependency]
readonly IUserInterfaceManager userInterfaceManager;
[Dependency]
readonly IStateManager stateManager;
[Dependency] private readonly IBaseClient _client;
[Dependency] private readonly IUserInterfaceManager userInterfaceManager;
[Dependency] private readonly IStateManager stateManager;
[Dependency] private readonly IClientNetManager _netManager;
private MainMenuControl _mainMenuControl;
private OptionsMenu OptionsMenu;
private Button ConnectButton;
// ReSharper disable once InconsistentNaming
private static readonly Regex IPv6Regex = new Regex(@"\[(.*:.*:.*)](?::(\d+))?");
@@ -50,7 +51,8 @@ namespace SS14.Client.State.States
var VBox = _mainMenuControl.GetChild("VBoxContainer");
VBox.GetChild<Button>("ExitButton").OnPressed += ExitButtonPressed;
VBox.GetChild<Button>("OptionsButton").OnPressed += OptionsButtonPressed;
VBox.GetChild<Button>("ConnectButton").OnPressed += ConnectButtonPressed;
ConnectButton = VBox.GetChild<Button>("ConnectButton");
ConnectButton.OnPressed += ConnectButtonPressed;
VBox.GetChild<LineEdit>("IPBox").OnTextEntered += IPBoxEntered;
_client.RunLevelChanged += RunLevelChanged;
@@ -66,6 +68,7 @@ namespace SS14.Client.State.States
public override void Shutdown()
{
_client.RunLevelChanged -= RunLevelChanged;
_netManager.ConnectFailed -= _onConnectFailed;
_mainMenuControl.Dispose();
OptionsMenu.Dispose();
@@ -94,6 +97,8 @@ namespace SS14.Client.State.States
private void TryConnect(string address)
{
ConnectButton.Disabled = true;
_netManager.ConnectFailed += _onConnectFailed;
try
{
ParseAddress(address, out var ip, out var port);
@@ -103,6 +108,7 @@ namespace SS14.Client.State.States
{
userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error.");
Logger.Warning(e.ToString());
_netManager.ConnectFailed -= _onConnectFailed;
}
}
@@ -112,6 +118,11 @@ namespace SS14.Client.State.States
{
stateManager.RequestStateChange<GameScreen>();
}
else if (args.NewLevel == ClientRunLevel.Initialize)
{
ConnectButton.Disabled = false;
_netManager.ConnectFailed -= _onConnectFailed;
}
}
private void ParseAddress(string address, out string ip, out ushort port)
@@ -131,6 +142,7 @@ namespace SS14.Client.State.States
return;
}
// See if the IP includes a port.
var split = address.Split(':');
ip = address;
@@ -151,6 +163,12 @@ namespace SS14.Client.State.States
}
}
private void _onConnectFailed(object _, NetConnectFailArgs args)
{
userInterfaceManager.Popup($"Failed to connect:\n{args.Reason}");
_netManager.ConnectFailed -= _onConnectFailed;
}
private class MainMenuControl : Control
{
protected override ResourcePath ScenePath => new ResourcePath("/Scenes/MainMenu/MainMenu.tscn");

View File

@@ -34,6 +34,7 @@ namespace SS14.Shared.Interfaces.Network
/// <summary>
/// Disconnects from the server. This does not Restart() the client networking. Make sure
/// to Initialize(true) networking before calling this.
/// Also cancels in-progress connection attempts.
/// </summary>
/// <param name="reason">The reason why disconnect was called.</param>
void ClientDisconnect(string reason);

View File

@@ -57,6 +57,11 @@ namespace SS14.Shared.Network
/// </summary>
public class NetConnectFailArgs : EventArgs
{
public NetConnectFailArgs(string reason)
{
Reason = reason;
}
public string Reason { get; }
}
}

View File

@@ -0,0 +1,317 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Lidgren.Network;
using SS14.Shared.Log;
using SS14.Shared.Utility;
using Timer = SS14.Shared.Timers.Timer;
namespace SS14.Shared.Network
{
public partial class NetManager
{
private CancellationTokenSource _cancelConnectTokenSource;
private readonly Dictionary<NetConnection, (CancellationTokenRegistration reg, TaskCompletionSource<string> tcs)> _awaitingStatusChange
= new Dictionary<NetConnection, (CancellationTokenRegistration, TaskCompletionSource<string>)>();
private readonly
Dictionary<NetConnection, (CancellationTokenRegistration, TaskCompletionSource<NetIncomingMessage>)>
_awaitingData =
new Dictionary<NetConnection, (CancellationTokenRegistration, TaskCompletionSource<NetIncomingMessage>)
>();
/// <inheritdoc />
public async void ClientConnect(string host, int port, string userNameRequest)
{
DebugTools.Assert(!IsServer, "Should never be called on the server.");
if (_clientConnectionState == ClientConnectionState.Connected)
{
throw new InvalidOperationException("The client is already connected to a server.");
}
if (_clientConnectionState != ClientConnectionState.NotConnecting)
{
throw new InvalidOperationException("A connect attempt is already in progress. Cancel it first.");
}
_cancelConnectTokenSource = new CancellationTokenSource();
var mainCancelToken = _cancelConnectTokenSource.Token;
_clientConnectionState = ClientConnectionState.ResolvingHost;
Logger.DebugS("net", "Attempting to connect to {0} port {1}", host, port);
// Get list of potential IP addresses for the domain.
var endPoints = await NetUtility.ResolveAsync(host);
if (mainCancelToken.IsCancellationRequested)
{
_clientConnectionState = ClientConnectionState.NotConnecting;
return;
}
if (endPoints == null)
{
OnConnectFailed($"Unable to resolve domain '{host}'");
_clientConnectionState = ClientConnectionState.NotConnecting;
return;
}
// Try to get an IPv6 and IPv4 address.
var ipv6 = endPoints.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetworkV6);
var ipv4 = endPoints.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork);
if (ipv4 == null && ipv6 == null)
{
OnConnectFailed($"Domain '{host}' has no associated IP addresses");
_clientConnectionState = ClientConnectionState.NotConnecting;
return;
}
_clientConnectionState = ClientConnectionState.EstablishingConnection;
IPAddress first;
IPAddress second = null;
if (ipv6 != null)
{
// If there's an IPv6 address try it first then the IPv4.
first = ipv6;
second = ipv4;
}
else
{
first = ipv4;
}
Logger.DebugS("net", "First attempt IP address is {0}, second attempt {1}", first, second);
NetPeer CreatePeerForIp(IPAddress address)
{
var config = _getBaseNetPeerConfig();
if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
config.LocalAddress = IPAddress.IPv6Any;
}
else
{
config.LocalAddress = IPAddress.Any;
}
var peer = new NetPeer(config);
peer.Start();
_netPeers.Add(peer);
return peer;
}
// Create first peer.
var firstPeer = CreatePeerForIp(first);
var firstConnection = firstPeer.Connect(new IPEndPoint(first, port));
NetPeer secondPeer = null;
NetConnection secondConnection = null;
string secondReason = null;
async Task ConnectSecondDelayed(CancellationToken cancellationToken)
{
DebugTools.AssertNotNull(second);
// Connecting via second peer is delayed by 25ms to give an advantage to IPv6, if it works.
await Task.Delay(25, cancellationToken);
if (cancellationToken.IsCancellationRequested)
{
return;
}
secondPeer = CreatePeerForIp(second);
secondConnection = secondPeer.Connect(new IPEndPoint(second, port));
secondReason = await AwaitStatusChange(secondConnection, cancellationToken);
}
NetPeer winningPeer;
NetConnection winningConnection;
string firstReason = null;
try
{
if (second != null)
{
// We have two addresses to try.
var cancellation = CancellationTokenSource.CreateLinkedTokenSource(mainCancelToken);
var firstPeerChanged = AwaitStatusChange(firstConnection, cancellation.Token);
var secondPeerChanged = ConnectSecondDelayed(cancellation.Token);
var firstChange = await Task.WhenAny(firstPeerChanged, secondPeerChanged);
if (firstChange == firstPeerChanged)
{
Logger.DebugS("net", "First peer status changed.");
// First peer responded first.
if (firstConnection.Status == NetConnectionStatus.Connected)
{
// First peer won!
Logger.DebugS("net", "First peer succeeded.");
cancellation.Cancel();
if (secondPeer != null)
{
secondPeer.Shutdown("First connection attempt won.");
_toCleanNetPeers.Add(secondPeer);
}
winningPeer = firstPeer;
winningConnection = firstConnection;
}
else
{
// First peer failed, try the second one I guess.
Logger.DebugS("net", "First peer failed.");
firstPeer.Shutdown("You failed.");
_toCleanNetPeers.Add(firstPeer);
firstReason = firstPeerChanged.Result;
await secondPeerChanged;
winningPeer = secondPeer;
winningConnection = secondConnection;
}
}
else
{
if (secondConnection.Status == NetConnectionStatus.Connected)
{
// Second peer won!
Logger.DebugS("net", "Second peer succeeded.");
cancellation.Cancel();
firstPeer.Shutdown("Second connection attempt won.");
winningPeer = secondPeer;
winningConnection = secondConnection;
}
else
{
// First peer failed, try the second one I guess.
Logger.DebugS("net", "Second peer failed.");
secondPeer.Shutdown("You failed.");
_toCleanNetPeers.Add(secondPeer);
firstReason = await firstPeerChanged;
winningPeer = firstPeer;
winningConnection = firstConnection;
}
}
}
else
{
// Only one address to try. Pretty straight forward.
firstReason = await AwaitStatusChange(firstConnection, mainCancelToken);
winningPeer = firstPeer;
winningConnection = firstConnection;
}
}
catch (TaskCanceledException)
{
firstPeer.Shutdown("Cancelled");
_toCleanNetPeers.Add(firstPeer);
if (secondPeer != null)
{
// ReSharper disable once PossibleNullReferenceException
secondPeer.Shutdown("Cancelled");
_toCleanNetPeers.Add(secondPeer);
}
_clientConnectionState = ClientConnectionState.NotConnecting;
return;
}
// winningPeer can still be failed at this point.
// If it is, neither succeeded. RIP.
if (winningConnection.Status != NetConnectionStatus.Connected)
{
winningPeer.Shutdown("You failed");
_toCleanNetPeers.Add(winningPeer);
OnConnectFailed(secondReason ?? firstReason);
_clientConnectionState = ClientConnectionState.NotConnecting;
return;
}
_clientConnectionState = ClientConnectionState.Handshake;
// We're connected start handshaking.
var userNameRequestMsg = winningPeer.CreateMessage(userNameRequest);
winningPeer.SendMessage(userNameRequestMsg, winningConnection, NetDeliveryMethod.ReliableOrdered);
try
{
// Await response.
var response = await AwaitData(winningConnection, mainCancelToken);
var receivedUsername = response.ReadString();
var channel = new NetChannel(this, winningConnection, new NetSessionId(receivedUsername));
_channels.Add(winningConnection, channel);
var confirmConnectionMsg = winningPeer.CreateMessage("ok");
winningPeer.SendMessage(confirmConnectionMsg, winningConnection, NetDeliveryMethod.ReliableOrdered);
}
catch (TaskCanceledException)
{
winningPeer.Shutdown("Cancelled");
_toCleanNetPeers.Add(secondPeer);
_clientConnectionState = ClientConnectionState.NotConnecting;
return;
}
catch (Exception e)
{
OnConnectFailed(e.Message);
Logger.ErrorS("net", "Exception during handshake: {0}", e);
winningPeer.Shutdown("Something happened.");
_toCleanNetPeers.Add(secondPeer);
_clientConnectionState = ClientConnectionState.NotConnecting;
return;
}
_clientConnectionState = ClientConnectionState.Connected;
Logger.DebugS("net", "Handshake completed, connection established.");
}
private Task<string> AwaitStatusChange(NetConnection connection, CancellationToken cancellationToken = default)
{
if (_awaitingStatusChange.ContainsKey(connection))
{
throw new InvalidOperationException();
}
var tcs = new TaskCompletionSource<string>();
CancellationTokenRegistration reg = default;
if (cancellationToken != default)
{
reg = cancellationToken.Register(() =>
{
_awaitingStatusChange.Remove(connection);
tcs.TrySetCanceled();
});
}
_awaitingStatusChange.Add(connection, (reg, tcs));
return tcs.Task;
}
private Task<NetIncomingMessage> AwaitData(NetConnection connection, CancellationToken cancellationToken= default)
{
if (_awaitingData.ContainsKey(connection))
{
throw new InvalidOperationException("Cannot await data twice.");
}
var tcs = new TaskCompletionSource<NetIncomingMessage>();
CancellationTokenRegistration reg = default;
if (cancellationToken != default)
{
reg = cancellationToken.Register(() =>
{
_awaitingData.Remove(connection);
tcs.TrySetCanceled();
});
}
_awaitingData.Add(connection, (reg, tcs));
return tcs.Task;
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Lidgren.Network;
using SS14.Shared.Configuration;
using SS14.Shared.Interfaces.Configuration;
@@ -29,7 +30,7 @@ namespace SS14.Shared.Network
/// <summary>
/// Manages all network connections and packet IO.
/// </summary>
public class NetManager : IClientNetManager, IServerNetManager, IDisposable
public partial class NetManager : IClientNetManager, IServerNetManager, IDisposable
{
private readonly Dictionary<Type, ProcessMessage> _callbacks = new Dictionary<Type, ProcessMessage>();
@@ -57,6 +58,7 @@ namespace SS14.Shared.Network
/// The list of network peers we are listening on.
/// </summary>
private readonly List<NetPeer> _netPeers = new List<NetPeer>();
private readonly List<NetPeer> _toCleanNetPeers = new List<NetPeer>();
/// <inheritdoc />
public int Port => _config.GetCVar<int>("net.port");
@@ -72,6 +74,8 @@ namespace SS14.Shared.Network
public bool IsRunning => _netPeers.Count != 0;
private ClientConnectionState _clientConnectionState;
public NetworkStats Statistics
{
get
@@ -221,6 +225,9 @@ namespace SS14.Shared.Network
}
_strings.Reset();
_cancelConnectTokenSource.Cancel();
_clientConnectionState = ClientConnectionState.NotConnecting;
}
public void ProcessPackets()
@@ -228,6 +235,7 @@ namespace SS14.Shared.Network
foreach (var peer in _netPeers)
{
NetIncomingMessage msg;
var recycle = true;
while ((msg = peer.ReadMessage()) != null)
{
switch (msg.MessageType)
@@ -253,7 +261,7 @@ namespace SS14.Shared.Network
break;
case NetIncomingMessageType.Data:
DispatchNetMessage(msg);
recycle = DispatchNetMessage(msg);
break;
case NetIncomingMessageType.StatusChanged:
@@ -269,43 +277,20 @@ namespace SS14.Shared.Network
break;
}
peer.Recycle(msg);
if (recycle)
{
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)
if (_toCleanNetPeers.Count != 0)
{
ClientDisconnect("Client left server.");
foreach (var peer in _toCleanNetPeers)
{
_netPeers.Remove(peer);
}
}
// 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 />
@@ -353,22 +338,40 @@ namespace SS14.Shared.Network
private void HandleStatusChanged(NetIncomingMessage msg)
{
var sender = msg.SenderConnection;
msg.ReadByte();
var reason = msg.ReadString();
Logger.DebugS("net", $"{sender.RemoteEndPoint}: Status changed to {sender.Status}");
if (_awaitingStatusChange.TryGetValue(sender, out var resume))
{
resume.Item1.Dispose();
resume.Item2.SetResult(reason);
_awaitingStatusChange.Remove(sender);
return;
}
switch (sender.Status)
{
case NetConnectionStatus.Connected:
HandleConnected(sender);
if (IsServer)
{
HandleHandshake(sender);
}
break;
case NetConnectionStatus.Disconnected:
if (_channels.ContainsKey(sender))
HandleDisconnect(msg);
else if (sender.RemoteUniqueIdentifier == 0
) // is this the best way to detect an unsuccessful connect?
if (_awaitingData.TryGetValue(sender, out var awaitInfo))
{
Logger.InfoS("net", $"{sender.RemoteEndPoint}: Failed to connect");
OnConnectFailed();
awaitInfo.Item1.Dispose();
awaitInfo.Item2.TrySetException(
new Exception($"Disconnected: {reason}"));
_awaitingData.Remove(sender);
}
if (_channels.ContainsKey(sender))
{
HandleDisconnect(msg);
}
break;
@@ -377,18 +380,33 @@ namespace SS14.Shared.Network
private void HandleApproval(NetIncomingMessage message)
{
var sender = message.SenderConnection;
var ip = sender.RemoteEndPoint;
var name = message.ReadString();
// TODO: Maybe preemptively refuse connections here in some cases?
if (message.SenderConnection.Status != NetConnectionStatus.RespondedAwaitingApproval)
{
// This can happen if the approval message comes in after the state changes to disconnected.
// In that case just ignore it.
return;
}
message.SenderConnection.Approve();
}
private async void HandleHandshake(NetConnection connection)
{
var userNamePacket = await AwaitData(connection);
var requestedUsername = userNamePacket.ReadString();
if (!UsernameHelpers.IsNameValid(requestedUsername))
{
connection.Disconnect("Username is invalid (contains illegal characters/too long).");
return;
}
var endPoint = connection.RemoteEndPoint;
var name = requestedUsername;
var origName = name;
var iterations = 1;
if (!UsernameHelpers.IsNameValid(name))
{
sender.Deny("Username is invalid (contains illegal characters/too long).");
}
while (_assignedSessions.Values.Any(u => u.Username == name))
while (_assignedSessions.Values.Any(u => u.Username == requestedUsername))
{
// This is shit but I don't care.
name = $"{origName}_{++iterations}";
@@ -396,30 +414,33 @@ namespace SS14.Shared.Network
var session = new NetSessionId(name);
if (OnConnecting(ip, session))
if (OnConnecting(endPoint, session))
{
_assignedSessions.Add(sender, session);
var msg = message.SenderConnection.Peer.CreateMessage();
_assignedSessions.Add(connection, session);
var msg = connection.Peer.CreateMessage();
msg.Write(name);
sender.Approve(msg);
connection.Peer.SendMessage(msg, connection, NetDeliveryMethod.ReliableOrdered);
}
else
{
sender.Deny("Server is full.");
connection.Disconnect("Sorry, denied. Why? Couldn't tell you, I didn't implement a deny reason.");
return;
}
var okMsg = await AwaitData(connection);
if (okMsg.ReadString() != "ok")
{
connection.Disconnect("You should say ok.");
return;
}
// Handshake complete!
HandleInitialHandshakeComplete(connection);
}
private void HandleConnected(NetConnection sender)
private void HandleInitialHandshakeComplete(NetConnection sender)
{
NetSessionId session;
if (IsClient)
{
session = new NetSessionId(sender.RemoteHailMessage.ReadString());
}
else
{
session = _assignedSessions[sender];
}
var session = _assignedSessions[sender];
var channel = new NetChannel(this, sender, session);
_channels.Add(sender, channel);
@@ -428,9 +449,7 @@ namespace SS14.Shared.Network
Logger.InfoS("net", $"{channel.RemoteEndPoint}: Connected");
// client is connected after string packet get received
if (IsServer)
OnConnected(channel);
OnConnected(channel);
}
private void HandleDisconnect(NetIncomingMessage message)
@@ -465,22 +484,31 @@ namespace SS14.Shared.Network
channel.Disconnect(reason);
}
private void DispatchNetMessage(NetIncomingMessage msg)
private bool DispatchNetMessage(NetIncomingMessage msg)
{
var peer = msg.SenderConnection.Peer;
if (peer.Status == NetPeerStatus.ShutdownRequested)
return;
return true;
if (peer.Status == NetPeerStatus.NotRunning)
return;
return true;
if (!IsConnected)
return;
return true;
if (_awaitingData.TryGetValue(msg.SenderConnection, out var info))
{
var (cancel, tcs) = info;
_awaitingData.Remove(msg.SenderConnection);
cancel.Dispose();
tcs.TrySetResult(msg);
return false;
}
if (msg.LengthBytes < 1)
{
Logger.WarningS("net", $"{msg.SenderConnection.RemoteEndPoint}: Received empty packet.");
return;
return true;
}
var id = msg.ReadByte();
@@ -488,13 +516,13 @@ namespace SS14.Shared.Network
if (!_strings.TryGetString(id, out string name))
{
Logger.WarningS("net", $"{msg.SenderConnection.RemoteEndPoint}: No string in table with ID {id}.");
return;
return true;
}
if (!_messages.TryGetValue(name, out Type packetType))
{
Logger.WarningS("net", $"{msg.SenderConnection.RemoteEndPoint}: No message with Name {name}.");
return;
return true;
}
var channel = GetChannel(msg.SenderConnection);
@@ -515,10 +543,11 @@ namespace SS14.Shared.Network
{
Logger.WarningS("net",
$"{msg.SenderConnection.RemoteEndPoint}: Received packet {id}:{name}, but callback was not registered.");
return;
return true;
}
callback?.Invoke(instance);
return true;
}
#region NetMessages
@@ -571,6 +600,7 @@ namespace SS14.Shared.Network
{
continue;
}
peer.SendMessage(packet, peer.Connections, method, 0);
}
}
@@ -629,9 +659,9 @@ namespace SS14.Shared.Network
return !args.Deny;
}
protected virtual void OnConnectFailed()
protected virtual void OnConnectFailed(string reason)
{
var args = new NetConnectFailArgs();
var args = new NetConnectFailArgs(reason);
ConnectFailed?.Invoke(this, args);
}
@@ -673,6 +703,34 @@ namespace SS14.Shared.Network
throw new ArgumentOutOfRangeException(nameof(group), group, null);
}
}
private enum ClientConnectionState
{
/// <summary>
/// We are not connected and not trying to get connected either. Quite lonely huh.
/// </summary>
NotConnecting,
/// <summary>
/// Resolving the DNS query for the address of the server.
/// </summary>
ResolvingHost,
/// <summary>
/// Attempting to establish a connection to the server.
/// </summary>
EstablishingConnection,
/// <summary>
/// Connection established, going through regular handshake business.
/// </summary>
Handshake,
/// <summary>
/// Connection is solid and handshake is done go wild.
/// </summary>
Connected
}
}
/// <summary>

View File

@@ -208,6 +208,7 @@
<Compile Include="Network\Messages\MsgViewVariablesReqData.cs" />
<Compile Include="Network\Messages\MsgViewVariablesReqSession.cs" />
<Compile Include="Network\NetChannelArgs.cs" />
<Compile Include="Network\NetManager.ClientConnect.cs" />
<Compile Include="Network\NetMessageArgs.cs" />
<Compile Include="Network\Messages\MsgChat.cs" />
<Compile Include="Network\Messages\MsgConCmd.cs" />