From e7db7d5bbabe7ec697cf4656c164224db949019e Mon Sep 17 00:00:00 2001 From: chimook Date: Sat, 11 Jul 2026 22:03:38 +0900 Subject: [PATCH] Sync EndCurrentJob in FloatMenuOptionProvider_DraftedMove.PawnGotoAction When a group of drafted pawns is given a move order, PawnGotoAction runs once per pawn on the client that issued the order. We already sync its TryTakeOrderedJob call, but it has another path: when a pawn is already standing on gotoLoc and its current job is Goto, it calls Pawn_JobTracker.EndCurrentJob directly. That call isn't synced, so the pawn stops only for the player who issued the order while it keeps walking for everyone else, causing a desync. Redirect that EndCurrentJob call to a synced wrapper through the existing DraftedMove_GotoFeedbackPatch transpiler, the same way the TryTakeOrderedJob call in the same method is already handled. --- Source/Client/Patches/Feedback.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Source/Client/Patches/Feedback.cs b/Source/Client/Patches/Feedback.cs index e2e18e1e3..bf6183b18 100644 --- a/Source/Client/Patches/Feedback.cs +++ b/Source/Client/Patches/Feedback.cs @@ -237,11 +237,15 @@ static class DraftedMove_GotoFeedbackPatch private static MethodInfo tryTakeOrderedJob = AccessTools.Method(typeof(Pawn_JobTracker), nameof(Pawn_JobTracker.TryTakeOrderedJob)); + private static MethodInfo endCurrentJob = + AccessTools.Method(typeof(Pawn_JobTracker), nameof(Pawn_JobTracker.EndCurrentJob)); + static IEnumerable Transpiler(IEnumerable instructions) { foreach (var inst in instructions) { if (inst.Calls(tryTakeOrderedJob)) inst.operand = ((Delegate)CustomTryTakeOrderedJob).Method; + else if (inst.Calls(endCurrentJob)) inst.operand = ((Delegate)CustomEndCurrentJob).Method; yield return inst; } } @@ -254,6 +258,17 @@ static bool CustomTryTakeOrderedJob(Pawn_JobTracker self, Job job, JobTag? tag = FleckMaker.Static(job.targetA.Cell, self.pawn.Map, FleckDefOf.FeedbackGoto); return false; } + + // PawnGotoAction can also stop a pawn without going through TryTakeOrderedJob: when the pawn is + // already standing on gotoLoc and its current job is Goto, it calls EndCurrentJob directly. That + // call isn't synced, so the pawn stops only for the player who issued the order while it keeps + // walking for everyone else, causing a desync. Sync it the same way as the TryTakeOrderedJob call. + [SyncMethod] + static void CustomEndCurrentJob(Pawn_JobTracker self, JobCondition condition, + bool startNewJob = true, bool canReturnToPool = true) + { + self.EndCurrentJob(condition, startNewJob, canReturnToPool); + } } }