diff --git a/Source/Client/Debug/DebugSync.cs b/Source/Client/Debug/DebugSync.cs
index 4b8609d8c..aaea73357 100644
--- a/Source/Client/Debug/DebugSync.cs
+++ b/Source/Client/Debug/DebugSync.cs
@@ -48,6 +48,12 @@ public static void HandleCmd(ByteReader data)
int selectedId = data.ReadInt32();
+ // Replay under the acting player's view, not the local one. Read before the node graph is
+ // rebuilt below, because node labels are allowed to depend on it -- the incident action's label
+ // embeds its target's name, so a wrong view here produces a path that matches nothing and the
+ // command silently does not run on this client.
+ WorldSelectedPatch.result = data.ReadBool();
+
if (Multiplayer.MapContext != null)
{
var thing = Multiplayer.ThingsById.GetValueSafe(selectedId);
@@ -115,6 +121,7 @@ public static void HandleCmd(ByteReader data)
MouseCellPatch.result = null;
MouseTilePatch.result = null;
+ WorldSelectedPatch.result = null;
Find.Selector.selected = prevSelected;
FieldRefs.worldSelected(Find.WorldSelector) = prevWorldSelected;
@@ -156,6 +163,11 @@ public static void SendCmd(DebugSource source, int hash, string path, Map map, I
else
writer.WriteInt32(Find.WorldSelector.SingleSelectedObject?.ID ?? -1);
+ // Which view the acting player had open. Debug actions may legitimately read it -- vanilla's
+ // incident action derives its entire target from it -- so replaying one faithfully means
+ // reproducing it, exactly as the cursor and selection above are reproduced.
+ writer.WriteBool(WorldRendererUtility.WorldSelected);
+
Multiplayer.WriterLog.AddCurrentNode(writer);
int mapId = map?.uniqueID ?? ScheduledCommand.Global;
diff --git a/Source/Client/Patches/Patches.cs b/Source/Client/Patches/Patches.cs
index 344833379..d6127fe52 100644
--- a/Source/Client/Patches/Patches.cs
+++ b/Source/Client/Patches/Patches.cs
@@ -192,6 +192,29 @@ static void Postfix(ref PlanetTile __result)
}
}
+ ///
+ /// Overrides whether the planet view is showing, while a debug command is being replayed.
+ ///
+ /// Companion to the cursor overrides above. Debug actions are allowed to read the interface -- vanilla's
+ /// incident action picks its target with
+ /// WorldRendererUtility.WorldSelected ? Find.WorldSelector.SingleSelectedObject : Find.CurrentMap --
+ /// so replaying one faithfully means reproducing what the acting player could see, not just where their
+ /// cursor was. Without this, the same command targets a caravan on a player looking at the planet and a
+ /// colony on a player looking at a map.
+ ///
+ [HarmonyPatch(typeof(WorldRendererUtility), nameof(WorldRendererUtility.WorldSelected), MethodType.Getter)]
+ public static class WorldSelectedPatch
+ {
+ /// Non-null only while a debug command is being replayed.
+ public static bool? result;
+
+ static void Postfix(ref bool __result)
+ {
+ if (result.HasValue)
+ __result = result.Value;
+ }
+ }
+
[HarmonyPatch(typeof(KeyBindingDef), nameof(KeyBindingDef.IsDownEvent), MethodType.Getter)]
public static class KeyIsDownPatch
{