Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Source/Client/Comp/BootstrapCoordinator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Verse;

namespace Multiplayer.Client.Comp;

public class BootstrapCoordinator : GameComponent
{
private int nextCheckTick;
private const int CheckIntervalTicks = 15;

public BootstrapCoordinator(Game game)
{
}

public override void GameComponentTick()
{
base.GameComponentTick();

var window = BootstrapConfiguratorWindow.Instance;
if (window == null)
return;

if (Find.TickManager != null && Find.TickManager.TicksGame < nextCheckTick)
return;

if (Find.TickManager != null)
nextCheckTick = Find.TickManager.TicksGame + CheckIntervalTicks;

window.BootstrapCoordinatorTick();
}

public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look(ref nextCheckTick, "mp_bootstrap_nextCheckTick", 0);
}
}
1 change: 1 addition & 0 deletions Source/Client/MultiplayerStatic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ static MultiplayerStatic()
MpConnectionState.SetImplementation(ConnectionStateEnum.ClientJoining, typeof(ClientJoiningState));
MpConnectionState.SetImplementation(ConnectionStateEnum.ClientLoading, typeof(ClientLoadingState));
MpConnectionState.SetImplementation(ConnectionStateEnum.ClientPlaying, typeof(ClientPlayingState));
MpConnectionState.SetImplementation(ConnectionStateEnum.ClientBootstrap, typeof(ClientBootstrapState));

MultiplayerData.CollectCursorIcons();

Expand Down
2 changes: 1 addition & 1 deletion Source/Client/Networking/State/ClientBaseState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void HandleTimeControl(ServerTimeControlPacket packet)
}

[TypedPacketHandler]
public void HandleDisconnected(ServerDisconnectPacket packet)
public virtual void HandleDisconnected(ServerDisconnectPacket packet)
{
ConnectionStatusListeners.TryNotifyAll_Disconnected(SessionDisconnectInfo.From(packet.reason,
new ByteReader(packet.data)));
Expand Down
42 changes: 42 additions & 0 deletions Source/Client/Networking/State/ClientBootstrapState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Multiplayer.Common;
using Multiplayer.Common.Networking.Packet;
using RimWorld;
using Verse;

namespace Multiplayer.Client;

[PacketHandlerClass(inheritHandlers: true)]
public class ClientBootstrapState(ConnectionBase connection) : ClientBaseState(connection)
{
[TypedPacketHandler]
public void HandleBootstrap(ServerBootstrapPacket packet)
{
Multiplayer.session?.ApplyBootstrapState(packet);

OnMainThread.Enqueue(() => BootstrapConfiguratorWindow.Instance?.ApplyBootstrapState(BootstrapServerState.FromPacket(packet)));
}

[TypedPacketHandler]
public override void HandleDisconnected(ServerDisconnectPacket packet)
{
if (packet.reason == MpDisconnectReason.BootstrapCompleted)
{
OnMainThread.Enqueue(() => Messages.Message(
"Bootstrap configuration completed. The server will now shut down; please restart it manually to start normally.",
MessageTypeDefOf.PositiveEvent, false));
}

OnMainThread.Enqueue(() =>
{
var window = Find.WindowStack.WindowOfType<BootstrapConfiguratorWindow>();
Multiplayer.session?.ClearBootstrapState();
if (window != null)
{
window.ResetTransientUiState(resetServerDrivenState: true);
Find.WindowStack.TryRemove(window);
}
});

base.HandleDisconnected(packet);
}
}
13 changes: 13 additions & 0 deletions Source/Client/Networking/State/ClientJoiningState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ public ClientJoiningState(ConnectionBase connection) : base(connection)
[TypedPacketHandler]
public new void HandleDisconnected(ServerDisconnectPacket packet) => base.HandleDisconnected(packet);

[TypedPacketHandler]
public void HandleBootstrap(ServerBootstrapPacket packet)
{
Multiplayer.session.ApplyBootstrapState(packet);
}

public override void StartState()
{
connection.Send(ClientProtocolPacket.Current());
Expand Down Expand Up @@ -120,6 +126,13 @@ void Complete()

void StartDownloading()
{
if (Multiplayer.session.bootstrapState.Enabled)
{
connection.ChangeState(ConnectionStateEnum.ClientBootstrap);
Find.WindowStack.Add(new BootstrapConfiguratorWindow(connection));
return;
}

connection.Send(Packets.Client_WorldRequest);
connection.ChangeState(ConnectionStateEnum.ClientLoading);
}
Expand Down
16 changes: 16 additions & 0 deletions Source/Client/Patches/BootstrapMapInitPatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using HarmonyLib;
using Verse;

namespace Multiplayer.Client;

[HarmonyPatch(typeof(MapComponentUtility), nameof(MapComponentUtility.FinalizeInit))]
static class BootstrapMapInitPatch
{
static void Postfix(Map map)
{
if (!BootstrapConfiguratorWindow.AwaitingBootstrapMapInit || BootstrapConfiguratorWindow.Instance == null)
return;

OnMainThread.Enqueue(() => BootstrapConfiguratorWindow.Instance.OnBootstrapMapInitialized());
}
}
13 changes: 13 additions & 0 deletions Source/Client/Patches/BootstrapRootPlayPatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using HarmonyLib;
using Verse;

namespace Multiplayer.Client;

[HarmonyPatch(typeof(Root_Play), nameof(Root_Play.Start))]
static class BootstrapRootPlayPatch
{
static void Postfix()
{
BootstrapConfiguratorWindow.Instance?.TryArmAwaitingBootstrapMapInit_FromRootPlay();
}
}
24 changes: 24 additions & 0 deletions Source/Client/Patches/BootstrapRootPlayUpdatePatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using HarmonyLib;
using Verse;

namespace Multiplayer.Client;

[HarmonyPatch(typeof(Root_Play), nameof(Root_Play.Update))]
static class BootstrapRootPlayUpdatePatch
{
private static int nextCheckFrame;
private const int CheckEveryFrames = 10;

static void Postfix()
{
var window = BootstrapConfiguratorWindow.Instance;
if (window == null)
return;

if (UnityEngine.Time.frameCount < nextCheckFrame)
return;

nextCheckFrame = UnityEngine.Time.frameCount + CheckEveryFrames;
window.TryArmAwaitingBootstrapMapInit_FromRootPlayUpdate();
}
}
18 changes: 18 additions & 0 deletions Source/Client/Patches/BootstrapStartedNewGamePatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using HarmonyLib;
using Verse;

namespace Multiplayer.Client;

[HarmonyPatch(typeof(GameComponentUtility), nameof(GameComponentUtility.StartedNewGame))]
static class BootstrapStartedNewGamePatch
{
static void Postfix()
{
var window = BootstrapConfiguratorWindow.Instance;
if (window == null)
return;

BootstrapConfiguratorWindow.AwaitingBootstrapMapInit = true;
OnMainThread.Enqueue(window.OnBootstrapMapInitialized);
}
}
14 changes: 14 additions & 0 deletions Source/Client/Session/BootstrapServerState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Multiplayer.Common.Networking.Packet;

namespace Multiplayer.Client;

public readonly record struct BootstrapServerState(bool Enabled, bool SettingsMissing, bool SaveMissing)
{
public static BootstrapServerState None => new(false, false, false);

public bool RequiresSettingsUpload => Enabled && SettingsMissing;
public bool RequiresSaveUpload => Enabled && !SettingsMissing && SaveMissing;

public static BootstrapServerState FromPacket(ServerBootstrapPacket packet) =>
new(packet.bootstrap, packet.settingsMissing, packet.saveMissing);
}
7 changes: 7 additions & 0 deletions Source/Client/Session/MultiplayerSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Multiplayer.Client.Networking;
using Multiplayer.Client.Util;
using Multiplayer.Common;
using Multiplayer.Common.Networking.Packet;
using RimWorld;
using Steamworks;
using UnityEngine;
Expand Down Expand Up @@ -54,6 +55,12 @@ public class MultiplayerSession : IConnectionStatusListener
public bool ArbiterPlaying => players.Any(p => p.type == PlayerType.Arbiter && p.status == PlayerStatus.Playing);

public IConnector connector;
public BootstrapServerState bootstrapState = BootstrapServerState.None;

public void ApplyBootstrapState(ServerBootstrapPacket packet) =>
bootstrapState = BootstrapServerState.FromPacket(packet);

public void ClearBootstrapState() => bootstrapState = BootstrapServerState.None;

public void Stop()
{
Expand Down
Loading
Loading