Skip to content

Commit 8e97cd3

Browse files
authored
Introduce ServerDisconnectPacket (#774)
* Add PacketHandlerClassAttribute Provides a cleaner way of dealing with common packets across different connection states * Introduce ServerDisconnectPacket Reduces reliance on a LiteNetLib-specific feature, unifies disconnection process across different transports, and allows to arbitrarily evolve the new typed packet.
1 parent d54766c commit 8e97cd3

16 files changed

+85
-65
lines changed

Source/Client/Networking/NetworkingInMemory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using Multiplayer.Common;
21
using System;
2+
using Multiplayer.Common;
33
using Verse;
44

55
namespace Multiplayer.Client.Networking
@@ -46,7 +46,7 @@ protected override void SendRaw(byte[] raw, bool reliable = true)
4646
});
4747
}
4848

49-
public override void Close(MpDisconnectReason reason, byte[] data = null)
49+
protected override void OnClose()
5050
{
5151
}
5252

Source/Client/Networking/NetworkingLiteNet.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ public static ClientLiteNetConnection Connect(string address, int port)
4444

4545
public void OnDisconnect(MpDisconnectReason reason, ByteReader data)
4646
{
47+
if (State == ConnectionStateEnum.Disconnected) return;
4748
ConnectionStatusListeners.TryNotifyAll_Disconnected(SessionDisconnectInfo.From(reason, data));
4849
Multiplayer.StopMultiplayer();
4950
}
5051

51-
public override void Close(MpDisconnectReason reason, byte[] data)
52+
protected override void OnClose()
5253
{
53-
base.Close(reason, data);
54+
base.OnClose();
5455
netManager.Stop();
5556
}
5657

@@ -81,7 +82,8 @@ public void OnPeerDisconnected(NetPeer peer, DisconnectInfo info)
8182
MpDisconnectReason reason;
8283
ByteReader reader;
8384

84-
if (info.AdditionalData.IsNull)
85+
// Fallback: should generally be handled by ClientBaseState.HandleDisconnected.
86+
if (info.AdditionalData.IsNull || info.AdditionalData.AvailableBytes == 0)
8587
{
8688
if (info.Reason is DisconnectReason.DisconnectPeerCalled or DisconnectReason.RemoteConnectionClose)
8789
reason = MpDisconnectReason.Generic;

Source/Client/Networking/NetworkingSteam.cs

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,13 @@ public void SendRawSteam(byte[] raw, bool reliable)
3838
ServerLog.Error($"Failed to send packet (len: {raw.Length}): {hex}");
3939
}
4040

41-
public override void Close(MpDisconnectReason reason, byte[] data = null)
42-
{
43-
if (State != ConnectionStateEnum.ClientSteam)
44-
Send(Packets.Special_Steam_Disconnect, GetDisconnectBytes(reason, data));
45-
}
46-
4741
public abstract void OnError(EP2PSessionError error);
4842

49-
public override string ToString()
43+
protected override void OnClose()
5044
{
51-
return $"SteamP2P ({remoteId}:{username})";
5245
}
46+
47+
public override string ToString() => $"SteamP2P ({remoteId}:{username})";
5348
}
5449

5550
public class SteamClientConn(CSteamID remoteId) : SteamBaseConn(remoteId, RandomChannelId(), 0), ITickableConnection
@@ -66,30 +61,12 @@ public void Tick()
6661
}
6762
}
6863

69-
protected override void HandleReceiveMsg(int msgId, int fragState, ByteReader reader, bool reliable)
70-
{
71-
if (msgId == (int)Packets.Special_Steam_Disconnect)
72-
{
73-
var info = SessionDisconnectInfo.From(reader.ReadEnum<MpDisconnectReason>(), reader);
74-
OnDisconnect(info);
75-
return;
76-
}
77-
78-
base.HandleReceiveMsg(msgId, fragState, reader, reliable);
79-
}
80-
8164
public override void OnError(EP2PSessionError error)
8265
{
8366
var title = error == EP2PSessionError.k_EP2PSessionErrorTimeout
8467
? "MpSteamTimedOut".Translate()
8568
: "MpSteamGenericError".Translate();
86-
87-
OnDisconnect(new SessionDisconnectInfo { titleTranslated = title });
88-
}
89-
90-
private void OnDisconnect(SessionDisconnectInfo info)
91-
{
92-
ConnectionStatusListeners.TryNotifyAll_Disconnected(info);
69+
ConnectionStatusListeners.TryNotifyAll_Disconnected(new SessionDisconnectInfo { titleTranslated = title });
9370
Multiplayer.StopMultiplayer();
9471
}
9572
}
@@ -104,17 +81,6 @@ public override void Send(Packets id, byte[] message, bool reliable = true)
10481
base.Send(id, message, reliable);
10582
}
10683

107-
protected override void HandleReceiveMsg(int msgId, int fragState, ByteReader reader, bool reliable)
108-
{
109-
if (msgId == (int)Packets.Special_Steam_Disconnect)
110-
{
111-
OnDisconnect();
112-
return;
113-
}
114-
115-
base.HandleReceiveMsg(msgId, fragState, reader, reliable);
116-
}
117-
11884
public override void OnError(EP2PSessionError error)
11985
{
12086
OnDisconnect();

Source/Client/Networking/State/ClientBaseState.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Multiplayer.Client.Networking;
12
using Multiplayer.Common;
23
using Multiplayer.Common.Networking.Packet;
34

@@ -7,15 +8,17 @@ public abstract class ClientBaseState(ConnectionBase connection) : MpConnectionS
78
{
89
protected MultiplayerSession Session => Multiplayer.session;
910

10-
protected void HandleKeepAlive(ServerKeepAlivePacket packet)
11+
[TypedPacketHandler]
12+
public void HandleKeepAlive(ServerKeepAlivePacket packet)
1113
{
1214
int ticksBehind = TickPatch.tickUntil - TickPatch.Timer;
1315

1416
connection.Send(new ClientKeepAlivePacket(packet.id, ticksBehind, TickPatch.Simulating, TickPatch.workTicks),
1517
false);
1618
}
1719

18-
protected void HandleTimeControl(ServerTimeControlPacket packet)
20+
[TypedPacketHandler]
21+
public void HandleTimeControl(ServerTimeControlPacket packet)
1922
{
2023
if (Multiplayer.session.remoteTickUntil >= packet.tickUntil) return;
2124

@@ -24,4 +27,12 @@ protected void HandleTimeControl(ServerTimeControlPacket packet)
2427
Multiplayer.session.remoteSentCmds = packet.sentCmds;
2528
Multiplayer.session.ProcessTimeControl();
2629
}
30+
31+
[TypedPacketHandler]
32+
public void HandleDisconnected(ServerDisconnectPacket packet)
33+
{
34+
ConnectionStatusListeners.TryNotifyAll_Disconnected(SessionDisconnectInfo.From(packet.reason,
35+
new ByteReader(packet.data)));
36+
Multiplayer.StopMultiplayer();
37+
}
2738
}

Source/Client/Networking/State/ClientJoiningState.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@
99

1010
namespace Multiplayer.Client
1111
{
12+
13+
[PacketHandlerClass(inheritHandlers: false)]
1214
public class ClientJoiningState : ClientBaseState
1315
{
1416
public ClientJoiningState(ConnectionBase connection) : base(connection)
1517
{
1618
}
1719

20+
[TypedPacketHandler]
21+
public new void HandleDisconnected(ServerDisconnectPacket packet) => base.HandleDisconnected(packet);
22+
1823
public override void StartState()
1924
{
2025
connection.Send(ClientProtocolPacket.Current());

Source/Client/Networking/State/ClientLoadingState.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using Ionic.Zlib;
55
using Multiplayer.Client.Saving;
66
using Multiplayer.Common;
7-
using Multiplayer.Common.Networking.Packet;
87
using Verse;
98

109
namespace Multiplayer.Client;
@@ -15,6 +14,7 @@ public enum LoadingState
1514
Downloading
1615
}
1716

17+
[PacketHandlerClass(inheritHandlers: true)]
1818
public class ClientLoadingState(ConnectionBase connection) : ClientBaseState(connection)
1919
{
2020
public LoadingState subState = LoadingState.Waiting;
@@ -139,10 +139,4 @@ public void HandleWorldData(ByteReader data)
139139
Log.Message($"Loaded game in {loadingMs}ms");
140140
connection.ChangeState(ConnectionStateEnum.ClientPlaying);
141141
}
142-
143-
[TypedPacketHandler]
144-
public new void HandleKeepAlive(ServerKeepAlivePacket packet) => base.HandleKeepAlive(packet);
145-
146-
[TypedPacketHandler]
147-
public new void HandleTimeControl(ServerTimeControlPacket packet) => base.HandleTimeControl(packet);
148142
}

Source/Client/Networking/State/ClientPlayingState.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,9 @@
99

1010
namespace Multiplayer.Client
1111
{
12+
[PacketHandlerClass(inheritHandlers: true)]
1213
public class ClientPlayingState(ConnectionBase connection) : ClientBaseState(connection)
1314
{
14-
[TypedPacketHandler]
15-
public new void HandleKeepAlive(ServerKeepAlivePacket packet) => base.HandleKeepAlive(packet);
16-
17-
[TypedPacketHandler]
18-
public new void HandleTimeControl(ServerTimeControlPacket packet) => base.HandleTimeControl(packet);
19-
2015
[TypedPacketHandler]
2116
public void HandleCommand(ServerCommandPacket packet)
2217
{

Source/Client/Saving/ReplayConnection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public override void HandleReceiveRaw(ByteReader data, bool reliable)
3030
{
3131
}
3232

33-
public override void Close(MpDisconnectReason reason, byte[] data)
33+
protected override void OnClose()
3434
{
3535
}
3636
}

Source/Client/Session/SessionDisconnectInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public static SessionDisconnectInfo From(MpDisconnectReason reason, ByteReader r
9494
disconnectInfo.titleTranslated ??= titleKey?.Translate();
9595
disconnectInfo.descTranslated ??= descKey?.Translate();
9696

97-
Log.Message($"Processed disconnect packet. Title: {disconnectInfo.titleTranslated} ({titleKey}), " +
97+
Log.Message($"Processed disconnect packet ({reason}). Title: {disconnectInfo.titleTranslated} ({titleKey}), " +
9898
$"description: {disconnectInfo.descTranslated} ({descKey})");
9999

100100
return disconnectInfo;

Source/Common/Networking/ConnectionBase.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,15 @@ private void ExecuteMessageHandler(PacketHandlerInfo handler, Packets packet, By
264264
}
265265
}
266266

267-
public abstract void Close(MpDisconnectReason reason, byte[]? data = null);
267+
public void Close(MpDisconnectReason reason, byte[]? data = null)
268+
{
269+
// State.IsServer check only used when disconnecting from a self-hosted local server
270+
if (State != ConnectionStateEnum.Disconnected && State.IsServer())
271+
Send(new ServerDisconnectPacket { reason = reason, data = data ?? [] });
272+
OnClose();
273+
}
274+
275+
protected abstract void OnClose();
268276

269277
/// Invoked after a keep alive timer arrives. Only used by the server
270278
public virtual void OnKeepAliveArrived(bool idMatched)

0 commit comments

Comments
 (0)