Skip to content

Instantly share code, notes, and snippets.

@Zetrith
Created October 21, 2023 10:54
Show Gist options
  • Save Zetrith/fb72009eacfb4df6725a966a3ad0dd44 to your computer and use it in GitHub Desktop.
Save Zetrith/fb72009eacfb4df6725a966a3ad0dd44 to your computer and use it in GitHub Desktop.
Multiplayer multifaction

The faction system is well integrated into the game and multifaction makes good use of it. Most of the work needs to be done in places where vanilla expects only one (player) faction.

Basically, RimWorld keeps a global variable Faction.OfPlayer storing the single player faction. Multiplayer creates additional player factions and then changes the value of this variable when necessary. The value of OfPlayer then depends on the context in which code is executing (you could say it's a form of dynamic scoping).

This "faction context" is set, for example, to the a colonist's faction when it begins executing its logic and then restored. In code:

void Pawn.Tick() {
  FactionContext.Push(this.Faction);
  // pawn logic
  FactionContext.Pop();
}

Some other places where the context is set:

  • top-level map ticking happens in the context of the faction which owns the map
  • planet ticking is wrapped in the context of a dummy spectator faction

FactionContext follows function calls so it naturally has a stack structure. It looks something like this:

void FactionContext.Push(Faction newPlayerFaction) {
  stack.Push(Faction.OfPlayer); // store current value
  Faction.OfPlayer = newPlayerFaction;
}

void FactionContext.Pop() {
  Faction.OfPlayer = stack.Pop(); // restore value
}

This works like the way vanilla sets and restores the RNG seed: Rand.PushState and Rand.PopState.

The last detail is that apart from setting Faction.OfPlayer, changing the player faction actually also sets some other fields to values created by Multiplayer per faction (where vanilla expects only one value):

void SetFaction(Map map, Faction newFaction) {
  Faction.OfPlayer = newFaction;

  map.areaManager = areaManagers[newFaction];
  world.researchManager = researchManagers[newFaction];
  // etc.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment