Patch
Runtime method patching: weaves a ByteBuddy Advice into a method named at runtime. Used only by execute_code scripts, via Patches.
onEnter inlines PatchBridge.fireEnter(slot, key, this, args) at the method's head; onExit inlines PatchBridge.fireExit(slot, key, this, args, returned, thrown) at every exit, including the exceptional one. The bridge indexes SEQ's slot to reach the registered game-side callback.
intercept and modify weave the writable blueprints instead — see Phase for what separates them, and PatchInterceptAdvice / PatchModifyAdvice for the bytecode each produces.
How a target is chosen
PatchTargets does the picking; this is why it picks that way. When the class is already loaded — the normal case — the target is resolved against the LIVE class: its hierarchy supplies the owners, and the mojmap name the script wrote is translated FORWARD over them (Signatures.runtimeNamesOf). Reflection seeing ground truth buys three things:
An override declared on a MOD class resolves. Its own name is in no mapping row, but climbing to the MC class it was inherited from reaches the row that does name it — which a single forward lookup on the script's class would miss, and which a reverse demap of the runtime name cannot do at all on an obf runtime, where one name serves methods jar-wide (see Mappings.reverseMethod).
The weave matcher is built from the resolved method's exact
(name, descriptor), so there is no second signature-comparison implementation that could disagree with the one used to pick the target."No such method" and "ambiguous" are found BEFORE anything is installed, so they throw as clean script errors listing the real candidates, instead of silently weaving nothing.
When the class is NOT loaded yet the patch still installs and waits — mod classes load late — but nothing can be verified at that point. See PatchHandle.targets for how that is reported.
Bridge injection lives in Instrumentations — see its class doc for the load-order constraint.
Types
InterceptHandler plus a read-only return observer, woven as the inner advice so a skipped body takes it with it. Fires are counted at the head only — one per call, as for every other phase.
Game-side handler: counts fires and forwards to the user callback. Implements the bootstrap Handler, whose phase methods all default to no-ops, so each subclass overrides only the ones its patch was woven for.
Head-side decision. Open only so AroundHandler can add the return observer to it.
Which advice blueprint is woven. at is where the decision is made, affects which ends a write of it can be seen at; conflictsWith derives from those two and nothing else. Named slug and not tag, which on Patches means the caller's coexistence discriminator.
Warm target: never called, only woven against. Non-void so the writable blueprints build a real return assigner.
Everything the per-type transform body needs that isn't the type itself. methodName is the mojmap name the script asked for, carried rather than looked back up: it is exact by construction, and on an obf runtime it is the only way the reported signature can name the method at all.
Holds the throwable ByteBuddy hands a listener, for PatchHandle.weaveError. Filtered to the patched type: one install's redefinition pass walks every loaded class.
Properties
ByteBuddy's default ignore minus two clauses. Its classloader one (isNull() or PlatformClassLoader) drops every bootstrap/platform target as onIgnored, which reaches PatchHandle as pending, unreadable against a class that never loaded. isSynthetic() reads modifiers, and the description this matches against resolves its NAME lazily and nothing else — so it would parse the class file of everything the name clauses below miss, i.e. every class the game loads, once per installed patch. Both matchers select one method on one FQN, so nothing synthetic reaches them anyway. The rest is verbatim: weaving ByteBuddy or the reflection accessors recurses into the weave, and PatchBridge — bootstrap-resident, so reachable only now — joins it for that same reason, being what every woven advice calls.
Minted per INSTALL, and it is BOTH the key's suffix and the bridge's registry slot — one number, so the two can never come to name different patches. Freshness, not collision avoidance: an id kept across a re-install answers null / -1 instead of the replacement's state, and a Probe channel named after it starts empty. It equally strands advice Patches.remove could not unweave, whose slot stays burned and reads null. Starts at 1, which is what leaves 0 free for warm's never-registered blueprints.
Functions
Which types the install's redefinition pass walks: the default walks getAllLoadedClasses() and matches every entry, so a pending install — which has no Class to name yet — still catches its target the moment it loads. Unweaving does not come through here; Unweave re-resolves the name against the live classes instead, so it sees whatever a pending patch eventually wove.
As onEnter, but cb may rewrite the arguments or skip the body — see PatchInterceptCallback.
The install call echoed back, deliberately NOT the resolved methods: the caller reads their own spelling with no namespace to undo, and the id cannot outgrow what they typed. What was woven is PatchHandle.targets.
As onExit, but cb may replace the return value — see PatchModifyCallback.
Patch the method(s) named methodName on className to fire cb on entry. params selects a single overload — see Patches.onEnter for its spelling — or null to weave every overload of that name. Callers pass mojmap class/method names. Returns a handle exposing the fire count, the woven targets, and the id Patches.remove takes to restore the original bytecode.
As onEnter, but woven at every exit — normal return and thrown exception alike.
The per-type transform body. It re-derives the matching methods from the type being transformed, so the PENDING path — which had nothing to check at install time — still enforces the one-signature rule here. It cannot throw usefully at this point: a ClassFileTransformer throwable is swallowed by the JVM and the class loads untransformed, and the caller returned long ago. So an ambiguous late resolution refuses to weave and says so, and PatchHandle.targets reports the empty result.