GameManager¶
Vanilla top-level game-manager singleton. Holds global state referenced by many subsystems.
CustomColors list¶
Source: F0027.
GameManager.Instance.CustomColors is a List<CustomColor> where each entry has a .Normal material. The index into this list is the canonical color identifier used throughout the mod.
Load finalize chain¶
GameManager.StartGame() is the post-deserialize entry point that flips the game from "loading" to playable. Body (verbatim, in order):
public static async UniTask StartGame()
{
GameState = GameState.Running;
GameTime = Time.time;
HelperHintsTextController.InitializePanel();
UpdateThingsOnGameStart();
StructureNetwork.StructureNetworksOnFinishedLoad();
Rocket.OnFinishedLoad();
SpaceMap.CleanUpOnFinishedLoad();
// ... InventoryManager parent-brain hookup, time scale, FoV, UI cleanup,
// NetworkServer.Host()/PopulateHostClient(), StationAutoSave reset,
// OrbitalSimulation.OnGameStarted(), Steam rich presence ...
}
UpdateThingsOnGameStart() iterates the global registry and dispatches per-Thing finalization:
public static void UpdateThingsOnGameStart()
{
OcclusionManager.AllThings.ForEach(UpdateThingsOnGameStartAction);
}
private static readonly Action<Thing> UpdateThingsOnGameStartAction = delegate(Thing thing)
{
if ((object)thing == null) return;
thing.OnFinishedLoad();
foreach (Interactable interactable in thing.Interactables)
{
if (interactable.JoinInProgressSync && (bool)interactable.Animator)
{
interactable.SetState();
thing.OnFinishedInteractionSync(interactable);
}
}
};
Implications for a mod that needs to run a one-shot pass after the world has fully deserialized:
GameStateis alreadyRunningat the momentUpdateThingsOnGameStart()is invoked. A guardGameManager.GameState != GameState.Runningis therefore not sufficient to distinguish "during load finalize" from "in-game running".Thing.OnFinishedLoadhas been called on every Thing by the timeUpdateThingsOnGameStart()returns. APostfixonGameManager.StartGame(or onUpdateThingsOnGameStartitself) is the earliest reliable hook for "every Thing's transforms, rotations, and side-car-restored state are settled". Hooking individualThing.OnFinishedLoadpostfixes only sees its own state; partner Things may not have had theirOnFinishedLoadrun yet.StructureNetwork.StructureNetworksOnFinishedLoad,Rocket.OnFinishedLoad,SpaceMap.CleanUpOnFinishedLoadrun sequentially after the per-Thing pass. A mod whose post-load pass depends on cable networks being formed should run afterStructureNetworksOnFinishedLoad; for transform-only work, hooking afterUpdateThingsOnGameStartis enough.StartGameisasync UniTask. A HarmonyPostfixreturning before the awaits will run synchronously after the body up to the first await. The firstawaitisNetworkServer.Host()(gated bySettings.CurrentData.StartLocalHost || IsBatchMode); for single-player and host-as-server, the Postfix fires after every synchronous step includingUpdateThingsOnGameStart. For pure-client joining a remote host,StartGamemay not be called the same way; the join path usesNetworkClient.ProcessJoinDatainstead (seeResearch/Protocols/PlayerConnectedThingFindTiming.md).
Verification history¶
- 2026-04-20: page created from the Research migration; verbatim content lifted from F0027. No conflicts.
- 2026-05-02: added "Load finalize chain" section with the verbatim
StartGamebody,UpdateThingsOnGameStartstatic, andUpdateThingsOnGameStartActiondelegate. Sourced from the 0.2.6228.27061 Assembly-CSharp decompile (lines 188908, 189593, 189647). Documents the order in which post-deserialize callbacks fire so a mod-side one-shot post-load pass can pick the right hook (Postfix onGameManager.StartGamefor transform-settled state across all Things; per-ThingOnFinishedLoadis too early when partners are involved).
Open questions¶
None at creation.