Skip to content

Commit

Permalink
Merge pull request #98 from ethanmoffat/network_disconnect
Browse files Browse the repository at this point in the history
Update networking to show dialog immediately when connection to the server is lost and gracefully return to initial game state
  • Loading branch information
ethanmoffat committed Jan 31, 2021
2 parents f587db2 + 4db38f5 commit c64020a
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 49 deletions.
39 changes: 14 additions & 25 deletions EOLib/Net/Communication/AsyncSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,48 +40,36 @@ public class AsyncSocket : IAsyncSocket
private readonly Socket _socket;
private bool _connected;

public bool Connected => BlockingIsConnected();

public AsyncSocket(AddressFamily family, SocketType type, ProtocolType protocol)
{
_socket = new Socket(family, type, protocol);
}

public async Task<int> SendAsync(byte[] data, CancellationToken ct)
public Task<int> SendAsync(byte[] data, CancellationToken ct)
{
var task = Task.Run(() => BlockingSend(data), ct);

await task;
return task.Result;
return Task.Run(() => BlockingSend(data), ct);
}

public async Task<byte[]> ReceiveAsync(int bytes, CancellationToken ct)
public Task<byte[]> ReceiveAsync(int bytes, CancellationToken ct)
{
var task = Task.Run(() => BlockingReceive(bytes), ct);

await task;
return task.Result;
return Task.Run(() => BlockingReceive(bytes), ct);
}

public async Task<bool> CheckIsConnectedAsync(CancellationToken ct)
public Task<bool> CheckIsConnectedAsync(CancellationToken ct)
{
var task = Task.Run(() => BlockingIsConnected(), ct);

await task;
return task.Result;
return Task.Run(() => BlockingIsConnected(), ct);
}

public async Task<ConnectResult> ConnectAsync(EndPoint endPoint, CancellationToken ct)
public Task<ConnectResult> ConnectAsync(EndPoint endPoint, CancellationToken ct)
{
var task = Task.Run(() => BlockingConnect(endPoint), ct);

await task;
return task.Result;
return Task.Run(() => BlockingConnect(endPoint), ct);
}

public async Task DisconnectAsync(CancellationToken ct)
public Task DisconnectAsync(CancellationToken ct)
{
var task = Task.Run(() => BlockingDisconnect(), ct);

await task;
return Task.Run(() => BlockingDisconnect(), ct);
}

private int BlockingSend(byte[] data)
Expand Down Expand Up @@ -133,7 +121,7 @@ private bool BlockingIsConnected()
var dataAvailable = _socket.Available != 0;
return _connected && (pollResult || dataAvailable);
}
catch(ObjectDisposedException)
catch (ObjectDisposedException)
{
_connected = false;
return false;
Expand Down Expand Up @@ -199,6 +187,7 @@ private void BlockingDisconnect()
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
Expand Down
2 changes: 2 additions & 0 deletions EOLib/Net/Communication/IAsyncSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace EOLib.Net.Communication
{
public interface IAsyncSocket : IDisposable
{
bool Connected { get; }

Task<int> SendAsync(byte[] data, CancellationToken ct);

Task<byte[]> ReceiveAsync(int bytes, CancellationToken ct);
Expand Down
2 changes: 2 additions & 0 deletions EOLib/Net/Communication/INetworkClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public interface INetworkClient : IDisposable
{
bool Connected { get; }

bool Started { get; }

Task<ConnectResult> ConnectToServer(string host, int port);

void Disconnect();
Expand Down
35 changes: 16 additions & 19 deletions EOLib/Net/Communication/NetworkClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,9 @@ public class NetworkClient : INetworkClient

private readonly CancellationTokenSource _backgroundReceiveCTS;

public bool Connected
{
get
{
using (var cts = new CancellationTokenSource())
{
cts.CancelAfter(5000);
var t = _socket.CheckIsConnectedAsync(cts.Token);
return !t.IsCanceled && t.Result;
}
}
}
public bool Connected => _socket.Connected;

public bool Started { get; private set; }

public NetworkClient(IPacketProcessActions packetProcessActions,
IPacketHandlingActions packetHandlingActions,
Expand Down Expand Up @@ -69,28 +60,34 @@ public async Task<ConnectResult> ConnectToServer(string host, int port)
using (var cts = new CancellationTokenSource(5000))
{
var task = _socket.ConnectAsync(endPoint, cts.Token);
await task;
await task.ConfigureAwait(false);

if (task.IsCanceled)
return ConnectResult.Timeout;

Started = task.Result == ConnectResult.Success;

return task.IsCanceled ? ConnectResult.Timeout : task.Result;
return task.Result;
}
}

public void Disconnect()
{
_socket.DisconnectAsync(CancellationToken.None);
_socket.DisconnectAsync(CancellationToken.None).ConfigureAwait(false);
Started = false;
}

public async Task RunReceiveLoopAsync()
{
while (!_backgroundReceiveCTS.IsCancellationRequested)
{
var lengthData = await _socket.ReceiveAsync(2, _backgroundReceiveCTS.Token);
var lengthData = await _socket.ReceiveAsync(2, _backgroundReceiveCTS.Token).ConfigureAwait(false);
if (_backgroundReceiveCTS.IsCancellationRequested || lengthData.Length != 2)
break;

var length = _numberEncoderService.DecodeNumber(lengthData);

var packetData = await _socket.ReceiveAsync(length, _backgroundReceiveCTS.Token);
var packetData = await _socket.ReceiveAsync(length, _backgroundReceiveCTS.Token).ConfigureAwait(false);
if (_backgroundReceiveCTS.IsCancellationRequested || packetData.Length != length)
break;

Expand All @@ -117,15 +114,15 @@ public async Task<int> SendAsync(IPacket packet, int timeout = 1500)
LogSentPacket(packet, true);
var bytesToSend = _packetProcessActions.EncodePacket(packet);
using (var cts = new CancellationTokenSource(timeout))
return await _socket.SendAsync(bytesToSend, cts.Token);
return await _socket.SendAsync(bytesToSend, cts.Token).ConfigureAwait(false);
}

public async Task<int> SendRawPacketAsync(IPacket packet, int timeout = 1500)
{
LogSentPacket(packet, false);
var bytesToSend = _packetProcessActions.EncodeRawPacket(packet);
using (var cts = new CancellationTokenSource(timeout))
return await _socket.SendAsync(bytesToSend, cts.Token);
return await _socket.SendAsync(bytesToSend, cts.Token).ConfigureAwait(false);
}

[Conditional("DEBUG")]
Expand Down
3 changes: 2 additions & 1 deletion EOLib/Net/Connection/BackgroundReceiveActions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Threading;
using AutomaticTypeMapper;
using EOLib.Net.Communication;
Expand Down Expand Up @@ -41,7 +42,7 @@ public void CancelBackgroundReceiveLoop()

private async void BackgroundLoop()
{
await Client.RunReceiveLoopAsync();
await Client.RunReceiveLoopAsync().ConfigureAwait(false);
}

private INetworkClient Client => _clientProvider.NetworkClient;
Expand Down
3 changes: 1 addition & 2 deletions EOLib/Net/Connection/NetworkConnectionActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ public async Task<ConnectResult> ReconnectToServer()

public void DisconnectFromServer()
{
if (Client.Connected)
Client.Disconnect();
Client.Disconnect();

_sequenceRepository.SequenceIncrement = 0;
_sequenceRepository.SequenceStart = 0;
Expand Down
9 changes: 9 additions & 0 deletions EndlessClient/Dialogs/Actions/ErrorDialogDisplayAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,14 @@ public void ShowLoginError(LoginReply loginError)
EOMessageBoxStyle.SmallDialogLargeHeader);
messageBox.ShowDialog();
}

public void ShowConnectionLost(bool isInGame)
{
var resource = isInGame ? DialogResourceID.CONNECTION_LOST_IN_GAME : DialogResourceID.CONNECTION_LOST_CONNECTION;
var style = isInGame ? EOMessageBoxStyle.SmallDialogSmallHeader : EOMessageBoxStyle.SmallDialogLargeHeader;

var messageBox = _messageBoxFactory.CreateMessageBox(resource, style: style);
messageBox.ShowDialog();
}
}
}
2 changes: 2 additions & 0 deletions EndlessClient/Dialogs/Actions/IErrorDialogDisplayAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ public interface IErrorDialogDisplayAction
void ShowException(EmptyPacketReceivedException ex);

void ShowLoginError(LoginReply loginError);

void ShowConnectionLost(bool isIngame);
}
}
26 changes: 24 additions & 2 deletions EndlessClient/Network/PacketHandlerGameComponent.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using EndlessClient.GameExecution;
using EndlessClient.Controllers;
using EndlessClient.Dialogs.Actions;
using EndlessClient.GameExecution;
using EOLib.Net.Communication;
using EOLib.Net.Handlers;
using Microsoft.Xna.Framework;

Expand All @@ -7,16 +10,35 @@ namespace EndlessClient.Network
public class PacketHandlerGameComponent : GameComponent
{
private readonly IOutOfBandPacketHandler _packetHandler;
private readonly INetworkClientProvider _networkClientProvider;
private readonly IGameStateProvider _gameStateProvider;
private readonly IErrorDialogDisplayAction _errorDialogDisplayAction;
private readonly IMainButtonController _mainButtonController;

public PacketHandlerGameComponent(IEndlessGame game,
IOutOfBandPacketHandler packetHandler)
IOutOfBandPacketHandler packetHandler,
INetworkClientProvider networkClientProvider,
IGameStateProvider gameStateProvider,
IErrorDialogDisplayAction errorDialogDisplayAction,
IMainButtonController mainButtonController)
: base((Game) game)
{
_packetHandler = packetHandler;
_networkClientProvider = networkClientProvider;
_gameStateProvider = gameStateProvider;
_errorDialogDisplayAction = errorDialogDisplayAction;
_mainButtonController = mainButtonController;
}

public override void Update(GameTime gameTime)
{
if (_networkClientProvider.NetworkClient.Started && !_networkClientProvider.NetworkClient.Connected)
{
var isInGame = _gameStateProvider.CurrentState == GameStates.PlayingTheGame;
_mainButtonController.GoToInitialStateAndDisconnect();
_errorDialogDisplayAction.ShowConnectionLost(isInGame);
}

_packetHandler.PollForPacketsAndHandle();

base.Update(gameTime);
Expand Down

0 comments on commit c64020a

Please sign in to comment.