Skip to content

Commit 426f98f

Browse files
committed
Reduce MultiplayerServer.initData API surface and simplify it
1 parent c2b0946 commit 426f98f

File tree

5 files changed

+36
-32
lines changed

5 files changed

+36
-32
lines changed

Source/Client/Networking/HostUtil.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,8 @@ private static void PrepareLocalServer(ServerSettings settings, bool fromReplay)
7474
localServer.startingTimer = TickPatch.Timer;
7575
}
7676

77-
localServer.initDataSource = new TaskCompletionSource<ServerInitData>();
78-
localServer.CompleteInitData(
79-
ServerInitData.Deserialize(new ByteReader(ClientJoiningState.PackInitData(settings.syncConfigs)))
80-
);
77+
localServer.initData =
78+
ServerInitData.Deserialize(new ByteReader(ClientJoiningState.PackInitData(settings.syncConfigs)));
8179
}
8280

8381
private static void PrepareGame()

Source/Common/MultiplayerServer.cs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,13 @@ static MultiplayerServer()
4848
public ServerSettings settings;
4949

5050
public ServerInitData? initData;
51-
public TaskCompletionSource<ServerInitData?> initDataSource = new();
52-
public InitDataState initDataState = InitDataState.Waiting;
51+
private TaskCompletionSource<ServerInitData?> initDataSource = new();
52+
public InitDataState InitDataState =>
53+
initData != null ? InitDataState.Complete :
54+
// started init data must've completed with null, meaning the client disconnected while waiting for the data,
55+
// so we are waiting again
56+
initDataSource.Task.IsCompleted ? InitDataState.Waiting :
57+
InitDataState.Requested;
5358

5459
public volatile bool running;
5560
public event Action<MultiplayerServer>? TickEvent;
@@ -245,16 +250,22 @@ public void RegisterChatCmd(string cmdName, ChatCmdHandler handler) =>
245250

246251
public void HandleChatCmd(IChatSource source, string cmd) => chatCmdManager.Handle(source, cmd);
247252

248-
public Task<ServerInitData?> InitData()
249-
{
250-
return initDataSource.Task;
251-
}
253+
public Task<ServerInitData?> InitData() => initDataSource.Task;
252254

253-
public void CompleteInitData(ServerInitData data)
255+
/// Can only start one init data at a time. A StartInitData is considered complete once
256+
/// TaskCompletionResult.SetResult is called. Until that time no new calls to StartInitData will succeed.
257+
public TaskCompletionSource<ServerInitData?> StartInitData()
254258
{
255-
initData = data;
256-
initDataState = InitDataState.Complete;
257-
initDataSource.SetResult(data);
259+
if (InitDataState != InitDataState.Waiting)
260+
throw new InvalidOperationException($"Can't start init data in state {InitDataState}");
261+
var currInitDataSource = initDataSource = new TaskCompletionSource<ServerInitData?>();
262+
currInitDataSource.Task.ContinueWith(task =>
263+
{
264+
if (currInitDataSource != initDataSource)
265+
ServerLog.Error("InitDataSource changed during StartInitData");
266+
initData = task.Result;
267+
}, TaskContinuationOptions.ExecuteSynchronously);
268+
return currInitDataSource;
258269
}
259270
}
260271

Source/Common/Networking/State/ServerJoiningState.cs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ protected override async Task RunState()
1414
HandleUsername(await Packet(Packets.Client_Username));
1515

1616
while (await Server.InitData() is null && await EndIfDead())
17-
if (Server.initDataState == InitDataState.Waiting)
17+
if (Server.InitDataState == InitDataState.Waiting)
1818
await RequestInitData();
1919

2020
connection.Send(Packets.Server_UsernameOk);
@@ -83,23 +83,20 @@ private void HandleUsername(ByteReader data)
8383

8484
private async Task RequestInitData()
8585
{
86-
Server.initDataState = InitDataState.Requested;
87-
Server.initDataSource = new TaskCompletionSource<ServerInitData?>();
88-
89-
Player.SendPacket(Packets.Server_InitDataRequest, ByteWriter.GetBytes(Server.settings.syncConfigs));
90-
91-
ServerLog.Verbose("Sent initial data request");
92-
93-
var initData = await PacketOrNull(Packets.Client_InitData).Fragmented();
94-
95-
if (initData != null)
86+
ByteReader? initData = null;
87+
var completionSource = Server.StartInitData();
88+
try
9689
{
97-
Server.CompleteInitData(ServerInitData.Deserialize(initData));
90+
Player.SendPacket(Packets.Server_InitDataRequest, ByteWriter.GetBytes(Server.settings.syncConfigs));
91+
92+
ServerLog.Verbose("Sent initial data request");
93+
initData = await PacketOrNull(Packets.Client_InitData).Fragmented();
9894
}
99-
else
95+
finally
10096
{
101-
Server.initDataState = InitDataState.Waiting;
102-
Server.initDataSource.SetResult(null);
97+
// Invoking StartInitData and abandoning the completion source in case of an exception would mean a server
98+
// restart is necessary to try and set init data again. Make sure the server is more graceful in that case.
99+
completionSource.SetResult(initData != null ? ServerInitData.Deserialize(initData) : null);
103100
}
104101
}
105102

Source/Server/Server.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
var server = MultiplayerServer.instance = new MultiplayerServer(settings)
2727
{
2828
running = true,
29-
initDataState = InitDataState.Waiting
3029
};
3130

3231
var consoleSource = new ConsoleSource();

Source/Tests/ServerTest.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public void Test()
3838
var timeoutWatch = Stopwatch.StartNew();
3939
while (true)
4040
{
41-
if (server.initDataState == InitDataState.Complete && server.playerManager.Players.Count == 0)
41+
if (server.InitDataState == InitDataState.Complete && server.playerManager.Players.Count == 0)
4242
break; // Success
4343

4444
if (timeoutWatch.ElapsedMilliseconds > 2000)
@@ -83,7 +83,6 @@ private MultiplayerServer MakeServer(int port)
8383
})
8484
{
8585
running = true,
86-
initDataState = InitDataState.Waiting
8786
};
8887

8988
server.worldData.savedGame = Array.Empty<byte>();

0 commit comments

Comments
 (0)