Skip to content

fix(mp): harden Select Similar exemption + add opt-in diagnostic (dev build for repro) - #29

Merged
keyz182 merged 3 commits into
mainfrom
fix/mp-select-similar-instrumented
Aug 5, 2026
Merged

fix(mp): harden Select Similar exemption + add opt-in diagnostic (dev build for repro)#29
keyz182 merged 3 commits into
mainfrom
fix/mp-select-similar-instrumented

Conversation

@keyz182

@keyz182 keyz182 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Draft. Rebased on top of #28's approach — not intended to merge until we have a reporter log confirming which failure mode we're on.

Background

  • v1.3.6 (unmerged, dev branch) shipped an unpatch-based Select Similar exemption. Reporter saw 3/3 designate methods unpatched in log but drags still failed.
  • fix: Select Similar still synced in multiplayer via base.DesignateMultiCell #28 rewrote it as a second-order Harmony patch on Multiplayer.Client.DesignatorPatches.Designate* (mirrors Multiplayer-Compatibility's AllowTool exemption). Reporter tested that too and saw the new sync bypassed on 3/3 designate hooks install-success line — but drag still failed.
  • Install succeeds; runtime exemption does not. We need one drag's worth of evidence to know why before writing a third fix.

What this changes

1. Harden SkipSyncForSelectSimilar arg resolution

[HarmonyArgument("__instance")] Designator designatorDesignator __0.

Multiplayer's DesignatorPatches.Designate* are static methods whose first parameter is literally named __instance. That collides with Harmony's __instance special-name (target-instance, which is null for a static target). Different Harmony 2.x versions have handled that collision differently. __0 is Harmony's unambiguous positional accessor for the target's first declared parameter and dodges the whole class of failure mode.

Same semantics, removes hypothesis "arg comes through as null" as a variable.

2. Opt-in diagnostic logging

New Settings.MpDebugLogging (default off), exposed under a new Diagnostics section in the mod settings UI. When on:

  • SkipSyncForSelectSimilar logs one line per invocation with method name, designator instance type, and chosen branch (bypass-sync vs let-mp-sync).
  • Designator_SelectSimilar.DesignateMultiCell logs at entry so we know whether the drag actually reaches the KAU override at all under MP.

Decision matrix from one test drag

SkipSync log fires? DesignateMultiCell log fires? Conclusion
No No Our detour isn't intercepting MP's prefix at runtime (wrapper/JIT issue)
Yes, decision=let-mp-sync No Arg still resolves wrong, or type identity mismatch
Yes, decision=bypass-sync No MP has a higher-layer sync path (input/DragBox) we haven't blocked
Yes, decision=bypass-sync Yes Exemption works — different symptom, need reporter to describe what actually fails

What this does NOT change

  • The overall approach (second-order prefix on DesignatorPatches) — same as AllowTool's reference pattern.
  • Success-sound handling — already covered on this branch by Settings.DesignateSuccessFeedbackSuppressed + explicit soundSucceeded.PlayOneShotOnCamera() in the drag override. No Designator.Finalize patch needed here, unlike AllowTool.

Test plan

  • Reporter installs this build (both 1.6/Assemblies/KeyzAllowUtilities.dll and Compatibility/rwmt.Multiplayer/1.6/Assemblies/KeyzAllowUtilities.Multiplayer.dll).
  • Enables Diagnostics → "Log Multiplayer Select Similar decisions" in mod settings.
  • Loads an MP save, activates Select Similar, does one drag.
  • Shares the [KAU MP] ... log lines from that session.
  • Use the matrix above to pick the next fix.
  • Non-MP smoke: Select Similar in a solo game continues to work (logs will fire when toggle is on, but behaviour must be unchanged).

CormacOConnor72 and others added 3 commits August 4, 2026 23:29
…tiCell

The unpatch approach removed Multiplayer's prefixes from the three
Designate* overrides declared on Designator_SelectSimilar, but
DesignateMultiCell calls base.DesignateMultiCell, and Multiplayer also
patches Verse.Designator's own Designate* methods (every declared
override on every subtype, base class included). A base call goes
straight through the detour on the base method, so drags were still
serialized and cancelled locally even with 3/3 overrides unpatched,
then replayed from the tick loop without the cursor/selection context
the filter needs — failing with "No Selectables".

Prefix Multiplayer.Client.DesignatorPatches.Designate* themselves
instead: they receive the designator instance for every patched route,
base calls included, so a single instance check covers all paths. Runs
before the MP.enabled gate since it needs no Multiplayer API. Same
pattern Multiplayer-Compatibility uses for Allow Tool's select similar.
PR #28's SkipSyncForSelectSimilar was reported by a tester as still failing
to prevent Select Similar drags from being command-synced, despite the
"sync bypassed on 3/3 designate hooks" install log firing successfully.
The install succeeds but the drag is not exempted at runtime.

Two changes to narrow the failure mode without a third speculative fix:

1. Swap [HarmonyArgument("__instance")] Designator designator for the
   positional Designator __0. Multiplayer's DesignatorPatches.Designate*
   are static methods whose first parameter is literally named __instance,
   which collides with Harmony's __instance special name for the
   instance-of-a-nonstatic-target (null for a static target). __0 is
   Harmony's unambiguous positional accessor for the target's first
   declared parameter and sidesteps the collision on every Harmony 2.x
   version KAU or its cohabiting mods might ship. Removes hypothesis 1
   (arg resolves to null) as a variable.

2. Add Settings.MpDebugLogging (default off, exposed under a new
   Diagnostics section in the mod settings UI). When on:
   - SkipSyncForSelectSimilar logs one line per interception with the
     patched method name, designator instance type, and chosen branch.
   - Designator_SelectSimilar.DesignateMultiCell logs at entry so we know
     whether a drag under MP actually reaches the KAU override or is
     intercepted at a higher layer.

The two logs together disambiguate between hypotheses 2 (MP intercepts
drag before DesignatorPatches — e.g. an input-layer or DragBox sync path)
and 3 (our detour on the prefix method fails to intercept the wrapper's
call). Reporter can flip the toggle, do one drag, share the log.
The compat folder was already loading in production (Discord reporter's
log shows "sync bypassed on 3/3 designate hooks" firing, which requires
the DLL to have loaded). No bug fix needed here — just tightening.

Change: match value flipped to lowercase "rwmt.multiplayer" to mirror
RimWorld's runtime-normalized packageId (ModLister.RunningModsListForReading
shows the lowercase form even when About.xml declares "rwmt.Multiplayer").
IfModActive is case-insensitive against the Active mod set in production,
so both casings work — this is defensive, not corrective.

Comment records the evidence so a future maintainer doesn't retrace the
same investigation. Note: RimMCP's game_start mod-list override loads
assemblies but leaves ModMetaData.Active=False, which breaks IfModActive
during local test — that's a RimMCP env quirk, not a KAU packaging bug.
@keyz182

keyz182 commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Local repro proved the exemption works

Traced through with RimMCP (five-mod list: base + Harmony + Multiplayer + KAU + RimMCP), joined an in-process MP session, force-loaded the compat DLL via REPL (the compat folder didn't auto-load under RimMCP's mod-list override — a RimMCP env quirk where game_start leaves ModMetaData.Active=False and breaks IfModActive in loadFolders.xml; production is unaffected). Then repro'd the reporter's exact scenario:

  1. Click a sandstone chunk (seeds similarTo)
  2. Activate Select Similar
  3. Drag rect over more chunks
  4. Release

Result: 25 chunks selected as expected. Log lines:

[KAU MP] DesignateMultiCell: designator=KeyzAllowUtilities.Designator_SelectSimilar isSelectSimilar=True decision=bypass-sync
[KAU MP] Designator_SelectSimilar.DesignateMultiCell entered
(x25) [KAU MP] DesignateSingleCell: ... decision=bypass-sync

The second-order patch on Multiplayer.Client.DesignatorPatches.Designate* is intercepting correctly, __0 is resolving to the right Designator instance, and the drag runs locally as intended.

Why does the reporter still see a failure?

They saw PR #28's sync bypassed on 3/3 designate hooks install line but a still-broken drag. Possibilities in order of likelihood:

  1. They tested PR fix: Select Similar still synced in multiplayer via base.DesignateMultiCell #28 as originally submitted — with [HarmonyArgument("__instance")] Designator designator. The target's parameter is literally named __instance, which is Harmony's special-name for the target-instance (null on a static target). Some Harmony 2.x versions may resolve the arg to null before HarmonyArgument gets a look-in, making designator is not Designator_SelectSimilar always true → return true → sync fires anyway. This branch's __0 accessor sidesteps that.
  2. Stale DLL — their install has a mixed-version Compatibility/rwmt.Multiplayer/1.6/Assemblies/ from a prior build. Delete + refresh.
  3. A third intercept surface we haven't hit. Surveyed MP source (Client/Patches/Designators.cs, Selector, DesignatorManager, DragBox, Feedback.cs, Determinism.cs) — the only Selector-facing hook is a postfix on Selector.Deselect that tracks deselected objects for feedback; it doesn't mutate selection state. No other selection- or drag-clearing path found.

Ask reporter to re-test this build

  • Build the branch (or grab the DLLs from the branch's 1.6/Assemblies/ and Compatibility/rwmt.Multiplayer/1.6/Assemblies/ verbatim — both must be updated together).
  • Enable Diagnostics → "Log Multiplayer Select Similar decisions" in mod settings.
  • Do one drag. Share the [KAU MP] log lines.

Decision matrix in the PR body maps their output → next fix. If they get decision=bypass-sync + DesignateMultiCell entered (matching my repro) and the drag STILL fails visually, then the failure is elsewhere entirely and we have concrete evidence to chase it.

@keyz182
keyz182 marked this pull request as ready for review August 5, 2026 18:37
@keyz182
keyz182 merged commit 418fea2 into main Aug 5, 2026
1 check passed
@keyz182
keyz182 deleted the fix/mp-select-similar-instrumented branch August 5, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants