Skip to content

Commit

Permalink
Fixed reconnect issue, added client disconnect packet
Browse files Browse the repository at this point in the history
  • Loading branch information
Extremelyd1 committed Mar 15, 2021
1 parent 1f4a8c3 commit 657d11d
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 106 deletions.
50 changes: 29 additions & 21 deletions HKMP/Game/ClientManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public ClientManager(NetworkManager networkManager, PlayerManager playerManager,

// Register packet handlers
packetManager.RegisterClientPacketHandler<ServerShutdownPacket>(PacketId.ServerShutdown, OnServerShutdown);
packetManager.RegisterClientPacketHandler<ClientPlayerDisconnectPacket>(PacketId.PlayerDisconnect, OnPlayerDisconnect);
packetManager.RegisterClientPacketHandler<PlayerEnterScenePacket>(PacketId.PlayerEnterScene,
OnPlayerEnterScene);
packetManager.RegisterClientPacketHandler<PlayerLeaveScenePacket>(PacketId.PlayerLeaveScene,
Expand Down Expand Up @@ -128,7 +129,7 @@ public void Disconnect(bool sendDisconnect = true) {
if (sendDisconnect) {
// First send the server that we are disconnecting
Logger.Info(this, "Sending PlayerDisconnect packet");
_netClient.SendTcp(new PlayerDisconnectPacket().CreatePacket());
_netClient.SendTcp(new ServerPlayerDisconnectPacket().CreatePacket());
}

// Then actually disconnect
Expand Down Expand Up @@ -215,6 +216,16 @@ private void OnServerShutdown(ServerShutdownPacket packet) {
// Disconnect without sending the server that we disconnect, because the server is shutting down anyway
Disconnect(false);
}

private void OnPlayerDisconnect(ClientPlayerDisconnectPacket packet) {
Logger.Info(this, $"Received PlayerDisconnect packet for ID: {packet.Id}");

// Destroy player object
_playerManager.DestroyPlayer(packet.Id);

// Destroy map icon
_mapManager.RemovePlayerIcon(packet.Id);
}

private void OnPlayerEnterScene(PlayerEnterScenePacket packet) {
// Read ID from packet
Expand Down Expand Up @@ -375,26 +386,23 @@ private void CheckHeartBeat() {
return;
}

// TODO: there might be a bug here, sometimes the client still times out
// due to it not receiving any UDP data

// If we have not received a heart beat recently
// if (_heartBeatReceiveStopwatch.ElapsedMilliseconds > ConnectionTimeout) {
// Logger.Info(this,
// $"We didn't receive a heart beat from the server in {ConnectionTimeout} milliseconds, disconnecting ({_heartBeatReceiveStopwatch.ElapsedMilliseconds})");
//
// Disconnect();
// return;
// }
//
// // Check whether it is time to send another heart beat to the server
// if (_heartBeatSendStopwatch.ElapsedMilliseconds > HeartBeatInterval) {
// _netClient.SendUdp(new ServerHeartBeatPacket().CreatePacket());
//
// // And reset the stopwatch so we know when to send again
// _heartBeatSendStopwatch.Reset();
// _heartBeatSendStopwatch.Start();
// }
if (_heartBeatReceiveStopwatch.ElapsedMilliseconds > ConnectionTimeout) {
Logger.Info(this,
$"We didn't receive a heart beat from the server in {ConnectionTimeout} milliseconds, disconnecting ({_heartBeatReceiveStopwatch.ElapsedMilliseconds})");

Disconnect();
return;
}

// Check whether it is time to send another heart beat to the server
if (_heartBeatSendStopwatch.ElapsedMilliseconds > HeartBeatInterval) {
_netClient.SendUdp(new ServerHeartBeatPacket().CreatePacket());

// And reset the stopwatch so we know when to send again
_heartBeatSendStopwatch.Reset();
_heartBeatSendStopwatch.Start();
}
}

private void OnHeartBeat(ClientHeartBeatPacket packet) {
Expand All @@ -414,7 +422,7 @@ private void OnApplicationQuit() {

// Send a disconnect packet before exiting the application
Logger.Info(this, "Sending PlayerDisconnect packet");
_netClient.SendTcp(new PlayerDisconnectPacket().CreatePacket());
_netClient.SendTcp(new ServerPlayerDisconnectPacket().CreatePacket());
_netClient.Disconnect();
}

Expand Down
2 changes: 1 addition & 1 deletion HKMP/Game/MapManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ private void CreatePlayerIcon(int id, Vector3 position) {
_mapIcons[id] = mapIcon;
}

private void RemovePlayerIcon(int id) {
public void RemovePlayerIcon(int id) {
if (!_mapIcons.ContainsKey(id)) {
Logger.Warn(this, $"Tried to remove player icon of ID: {id}, but it didn't exist");
return;
Expand Down
94 changes: 48 additions & 46 deletions HKMP/Game/Server/ServerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public ServerManager(NetworkManager networkManager, Game.Settings.GameSettings g
packetManager.RegisterServerPacketHandler<PlayerChangeScenePacket>(PacketId.PlayerChangeScene, OnClientChangeScene);
packetManager.RegisterServerPacketHandler<ServerPlayerUpdatePacket>(PacketId.PlayerUpdate, OnPlayerUpdate);
packetManager.RegisterServerPacketHandler<ServerPlayerAnimationUpdatePacket>(PacketId.PlayerAnimationUpdate, OnPlayerUpdateAnimation);
packetManager.RegisterServerPacketHandler<PlayerDisconnectPacket>(PacketId.PlayerDisconnect, OnPlayerDisconnect);
packetManager.RegisterServerPacketHandler<ServerPlayerDisconnectPacket>(PacketId.PlayerDisconnect, OnPlayerDisconnect);
packetManager.RegisterServerPacketHandler<ServerPlayerDeathPacket>(PacketId.PlayerDeath, OnPlayerDeath);
packetManager.RegisterServerPacketHandler<ServerHeartBeatPacket>(PacketId.HeartBeat, OnHeartBeat);
packetManager.RegisterServerPacketHandler<ServerDreamshieldSpawnPacket>(PacketId.DreamshieldSpawn, OnDreamshieldSpawn);
Expand Down Expand Up @@ -271,9 +271,10 @@ private void OnClientChangeScene(int id, PlayerChangeScenePacket packet) {
_playerData[id] = playerData;
}

// TODO: implement similar system as client side, where we can update clients in batches
private void OnPlayerUpdate(int id, ServerPlayerUpdatePacket packet) {
if (!_playerData.ContainsKey(id)) {
Logger.Warn(this, $"Received PlayerPositionUpdate packet, but player with ID {id} is not in mapping");
Logger.Warn(this, $"Received PlayerUpdate packet, but player with ID {id} is not in mapping");
return;
}

Expand All @@ -290,16 +291,28 @@ private void OnPlayerUpdate(int id, ServerPlayerUpdatePacket packet) {
_playerData[id].LastMapLocation = mapPosition;

// Create the packet in advance
var positionUpdatePacket = new ClientPlayerUpdatePacket {
var playerUpdatePacket = new ClientPlayerUpdatePacket {
Id = (ushort) id,
Position = position,
Scale = scale,
MapPosition = mapPosition
};
positionUpdatePacket.CreatePacket();
playerUpdatePacket.CreatePacket();

// If there is a setting enabled that needs the broadcast of map positions, we send the update
// packet to every connected player. Otherwise, we only send it to players in the same scene.
if (_gameSettings.AlwaysShowMapIcons || _gameSettings.OnlyBroadcastMapIconWithWaywardCompass) {
// Send the update packet to each player, since it includes the map position update
foreach (var idScenePair in _playerData) {
if (idScenePair.Key == id) {
continue;
}

// Send the packet to all clients in the same scene
SendPacketToClientsInSameScene(positionUpdatePacket, false, currentScene, id);
_netServer.SendUdp(idScenePair.Key, playerUpdatePacket);
}
} else {
SendPacketToClientsInSameScene(playerUpdatePacket, false, currentScene, id);
}
}

private void OnPlayerUpdateAnimation(int id, ServerPlayerAnimationUpdatePacket packet) {
Expand Down Expand Up @@ -350,30 +363,19 @@ private void OnPlayerDisconnect(int id) {
Logger.Warn(this, $"Player disconnect, but player with ID {id} is not in mapping");
return;
}

// Get the scene that client was in while disconnecting
var currentScene = _playerData[id].CurrentScene;

// Create a PlayerLeaveScene packet containing the ID
// of the player disconnecting
var leaveScenePacket = new PlayerLeaveScenePacket {

// Send a player disconnect packet
var playerDisconnectPacket = new ClientPlayerDisconnectPacket {
Id = id
};
leaveScenePacket.CreatePacket();

foreach (var idScenePair in _playerData) {
if (idScenePair.Key == id) {
continue;
}

// Send the packet to all clients in the same scene
SendPacketToClientsInSameScene(leaveScenePacket, true, currentScene, id);

// // Also create a MapUpdate packet containing the ID
// // of the player disconnecting and an empty location
// var mapUpdatePacket = new ClientPlayerMapUpdatePacket {
// Id = id,
// Position = Vector3.zero
// };
// mapUpdatePacket.CreatePacket();
//
// // We might as well broadcast this over TCP as it doesn't happen often and does not require speed
// _netServer.BroadcastTcp(mapUpdatePacket);
_netServer.SendTcp(idScenePair.Key, playerDisconnectPacket.CreatePacket());
}

// Now remove the client from the player data mapping
_playerData.Remove(id);
Expand Down Expand Up @@ -486,25 +488,25 @@ private void CheckHeartBeat() {
}

// For each connected client, check whether a heart beat has been received recently
// foreach (var idPlayerDataPair in _playerData) {
// if (idPlayerDataPair.Value.HeartBeatStopwatch.ElapsedMilliseconds > ConnectionTimeout) {
// // The stopwatch has surpassed the connection timeout value, so we disconnect the client
// var id = idPlayerDataPair.Key;
// Logger.Info(this,
// $"Didn't receive heart beat from player {id} in {ConnectionTimeout} milliseconds, dropping client");
// OnPlayerDisconnect(id);
// }
// }
//
// // If it is time to send another heart beat to the clients
// if (_heartBeatSendStopwatch.ElapsedMilliseconds > HeartBeatInterval) {
// // Create and broadcast the heart beat over UDP
// _netServer.BroadcastUdp(new ClientHeartBeatPacket().CreatePacket());
//
// // And reset the timer, so we know when to send the next
// _heartBeatSendStopwatch.Reset();
// _heartBeatSendStopwatch.Start();
// }
foreach (var idPlayerDataPair in _playerData) {
if (idPlayerDataPair.Value.HeartBeatStopwatch.ElapsedMilliseconds > ConnectionTimeout) {
// The stopwatch has surpassed the connection timeout value, so we disconnect the client
var id = idPlayerDataPair.Key;
Logger.Info(this,
$"Didn't receive heart beat from player {id} in {ConnectionTimeout} milliseconds, dropping client");
OnPlayerDisconnect(id);
}
}

// If it is time to send another heart beat to the clients
if (_heartBeatSendStopwatch.ElapsedMilliseconds > HeartBeatInterval) {
// Create and broadcast the heart beat over UDP
_netServer.BroadcastUdp(new ClientHeartBeatPacket().CreatePacket());

// And reset the timer, so we know when to send the next
_heartBeatSendStopwatch.Reset();
_heartBeatSendStopwatch.Start();
}
}

private void OnHeartBeat(int id, ServerHeartBeatPacket packet) {
Expand Down
24 changes: 10 additions & 14 deletions HKMP/Networking/Client/UdpNetClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ public class UdpNetClient {

private readonly Stopwatch _sendStopwatch;

private readonly Thread _sendThread;

// The current send rate in milliseconds between sending packets
private int _currentSendRate = HighSendRate;

Expand Down Expand Up @@ -82,11 +80,11 @@ public UdpNetClient() {
_belowThresholdStopwatch = new Stopwatch();
_currentCongestionStopwatch = new Stopwatch();

_sendThread = new Thread(() => {
new Thread(() => {
while (true) {
Send();
SendPlayerUpdate();
}
});
}).Start();
}

public void RegisterOnReceive(OnReceive onReceive) {
Expand All @@ -107,8 +105,6 @@ public void Connect(string host, int port, int localPort) {
_currentCongestionStopwatch.Reset();
_currentCongestionStopwatch.Start();

_sendThread.Start();

// Reset some congestion related values
_averageRtt = 0f;
_currentSwitchTimeThreshold = 10000;
Expand Down Expand Up @@ -149,6 +145,7 @@ private void OnReceive(IAsyncResult result) {
foreach (var packet in packets) {
// Logger.Info(this, $"Received packet from server");

// TODO: use the player update packets that we already receive as acknowledgements
if (packet.IsAckPacket()) {
var sequenceNumber = packet.ReadSequenceNumber();
// Logger.Info(this, $"Packet is ack, seq: {sequenceNumber}");
Expand Down Expand Up @@ -259,8 +256,6 @@ public void Disconnect() {

_udpClient.Close();
_udpClient = null;

_sendThread.Abort();

_sendStopwatch.Reset();

Expand Down Expand Up @@ -295,7 +290,12 @@ public void Send(Packet.Packet packet) {
_udpClient.BeginSend(packet.ToArray(), packet.Length(), null, null);
}

private void Send() {
private void SendPlayerUpdate() {
if (_udpClient?.Client == null) {
Thread.Sleep(100);
return;
}

if (!_udpClient.Client.Connected) {
Logger.Error(this, "Tried sending packet, but UDP was not connected");
Thread.Sleep(100);
Expand All @@ -315,12 +315,8 @@ private void Send() {
_currentUpdatePacket.SequenceNumber = _sequenceNumber;

packet = _currentUpdatePacket.CreatePacket();

// Logger.Info(this, $"Animation: {_currentUpdatePacket.AnimationClipName}, {_currentUpdatePacket.AnimationEffectName}");
}

// Logger.Info(this, $"Sending new update packet, seq: {_sequenceNumber}");

// Before we add another item to our queue, we check whether some
// already exceed the maximum expected RTT
foreach (var seqStopwatchPair in new Dictionary<ushort, Stopwatch>(_sentQueue)) {
Expand Down
9 changes: 9 additions & 0 deletions HKMP/Networking/Packet/Custom/ClientPlayerDisconnectPacket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace HKMP.Networking.Packet.Custom {
public class ClientPlayerDisconnectPacket : GenericClientPacket {
public ClientPlayerDisconnectPacket() : base(PacketId.PlayerDisconnect) {
}

public ClientPlayerDisconnectPacket(Packet packet) : base(PacketId.PlayerDisconnect, packet) {
}
}
}
23 changes: 0 additions & 23 deletions HKMP/Networking/Packet/Custom/PlayerDisconnectPacket.cs

This file was deleted.

10 changes: 10 additions & 0 deletions HKMP/Networking/Packet/Custom/ServerPlayerDisconnectPacket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace HKMP.Networking.Packet.Custom {
public class ServerPlayerDisconnectPacket : GenericServerPacket {

public ServerPlayerDisconnectPacket() : base(PacketId.PlayerDisconnect) {
}

public ServerPlayerDisconnectPacket(Packet packet) : base(PacketId.PlayerDisconnect, packet) {
}
}
}
4 changes: 3 additions & 1 deletion HKMP/Networking/Packet/PacketManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ private IPacket InstantiateClientPacket(PacketId packetId, Packet packet) {
return new AcknowledgePacket(packet);
case PacketId.HeartBeat:
return new ClientHeartBeatPacket(packet);
case PacketId.PlayerDisconnect:
return new ClientPlayerDisconnectPacket(packet);
case PacketId.ServerShutdown:
return new ServerShutdownPacket(packet);
case PacketId.PlayerEnterScene:
Expand Down Expand Up @@ -285,7 +287,7 @@ private IPacket InstantiateServerPacket(PacketId packetId, Packet packet) {
case PacketId.HeartBeat:
return new ServerHeartBeatPacket(packet);
case PacketId.PlayerDisconnect:
return new PlayerDisconnectPacket(packet);
return new ServerPlayerDisconnectPacket(packet);
case PacketId.PlayerChangeScene:
return new PlayerChangeScenePacket(packet);
case PacketId.PlayerUpdate:
Expand Down
8 changes: 8 additions & 0 deletions HKMP/Networking/TcpNetClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,18 @@ private void FinishConnectionSetup() {
Logger.Info(this, "Connection success, setting up network stream");

_stream = _tcpClient.GetStream();
if (_stream == null) {
Logger.Error(this, "Connection failed, could not get network stream");

_onConnectFailed?.Invoke();
return;
}

_receivedData = new byte[MaxBufferSize];

_stream.BeginRead(_receivedData, 0, MaxBufferSize, OnReceive, null);

Logger.Info(this, "Network stream setup, listening for TCP data");

// Invoke callback if it exists
_onConnect?.Invoke();
Expand Down

0 comments on commit 657d11d

Please sign in to comment.