### NeoForge Setup (NeoGradle) Source: https://github.com/llamalad7/mixinextras/blob/master/README.MD Configure MixinExtras for NeoForge with NeoGradle, specifying version ranges. Compatible with NeoForge 20.2.84+. ```gradle dependencies { implementation(jarJar("io.github.llamalad7:mixinextras-neoforge:0.5.4")) { jarJar.ranged(it, "[0.5.4,)") } } ``` -------------------------------- ### ShadowJar Setup for Other Platforms Source: https://github.com/llamalad7/mixinextras/blob/master/README.MD Configure ShadowJar for platforms not covered by specific setups. Relocate MixinExtras package and merge service files. ```gradle plugins { id "com.github.johnrengelman.shadow" version "8.1.0" } configurations { implementation.extendsFrom shadow } dependencies { shadow(annotationProcessor("io.github.llamalad7:mixinextras-common:0.5.4")) } shadowJar { configurations = [project.configurations.shadow] relocate("com.llamalad7.mixinextras", "your.package.goes.here.mixinextras") mergeServiceFiles() // Very important! } ``` -------------------------------- ### NeoForge Setup (ModDevGradle) Source: https://github.com/llamalad7/mixinextras/blob/master/README.MD Include MixinExtras for NeoForge using ModDevGradle. Compatible with NeoForge 20.2.84+ or manage versions manually. ```gradle dependencies { implementation(jarJar("io.github.llamalad7:mixinextras-neoforge:0.5.4")) } ``` -------------------------------- ### Forge 1.18.2+ Setup (ForgeGradle) Source: https://github.com/llamalad7/mixinextras/blob/master/README.MD Set up MixinExtras for Forge 1.18.2+ with ForgeGradle, including common annotation processor and versioned jarJar. ```gradle dependencies { compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.5.4")) implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.5.4")) { jarJar.ranged(it, "[0.5.4,)") } } ``` -------------------------------- ### Code Diff Example Source: https://github.com/llamalad7/mixinextras/wiki/ModifyReceiver Illustrates the transformation applied by the ModifyReceiver annotation, showing how the original call is rerouted through the handler method. ```diff - object1.setX(10); + changeObject(object1, 10).setX(10); ``` -------------------------------- ### Fabric/Quilt Setup Source: https://github.com/llamalad7/mixinextras/blob/master/README.MD Add MixinExtras as an annotation processor for Fabric or Quilt. Ensure FabricLoader 0.15.0+ is used or manage versions manually. ```gradle dependencies { include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:0.5.4"))) } ``` -------------------------------- ### Example: Sharing an Argument with @Share Source: https://github.com/llamalad7/mixinextras/wiki/Share This example demonstrates sharing an argument from one handler method to another using @Share. The @ModifyArg method saves the argument, and the @Inject method retrieves it. ```java @ModifyArg(method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;renderSomething(I)V")) private int saveRenderSomethingArg(int arg, @Share("arg") LocalIntRef argRef) { argRef.set(arg); return arg; } ``` ```java @Inject(method = "targetMethod", at = @At("TAIL")) private void renderCustomThing(CallbackInfo ci, @Share("arg") LocalIntRef argRef) { this.renderSomethingCustom(argRef.get()); } ``` -------------------------------- ### Forge 1.18.2+ Setup (Architectury Loom) Source: https://github.com/llamalad7/mixinextras/blob/master/README.MD Integrate MixinExtras with Forge 1.18.2+ using Architectury Loom, including the common annotation processor. ```gradle dependencies { compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.5.4")) implementation(include("io.github.llamalad7:mixinextras-forge:0.5.4")) } ``` -------------------------------- ### Wrap Method Example Source: https://github.com/llamalad7/mixinextras/wiki/WrapMethod Wraps a method to return a default value instead of throwing an exception. The handler method receives the original arguments and an Operation to call the original method. ```java public int riskyCalculation(String someParam) throws SomeException { // Some logic using someParam } ``` ```java @WrapMethod(method = "riskyCalculation") private int guardRiskyCalculation(String someParam, Operation original) { try { return original.call(someParam); } catch (SomeException ignored) { return 0; } } ``` -------------------------------- ### Wrap Operation Example Source: https://github.com/llamalad7/mixinextras/wiki/WrapOperation Example of wrapping a method call to conditionally bypass it. The original method is called via `original.call()` if the bypass condition is not met. This preserves the original operation's behavior and return type. ```java int number = this.expensiveCalculation(flag); ``` ```java @WrapOperation( method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;expensiveCalculation(Z)I") ) private int bypassExpensiveCalculationIfNecessary(TargetClass instance, boolean flag, Operation original) { if (YourMod.bypassExpensiveCalculation) { return 10; } else { return original.call(instance, flag); } } ``` -------------------------------- ### Code modification example Source: https://github.com/llamalad7/mixinextras/wiki/Expressions This diff shows how the original if condition is modified to include the result of the 'yourHandler' method, which incorporates the custom expression logic. ```diff - if (this.fallDistance > 0.0F) { + if (yourHandler(this.fallDistance > 0.0F)) { ... ``` -------------------------------- ### Capture Mutable Local Variable References with @Local Source: https://github.com/llamalad7/mixinextras/wiki/Local Utilize @Local with reference types like LocalRef and LocalIntRef to capture mutable references to local variables. This allows modifying the original variables via get() and set() methods. This example modifies player name and color. ```java String playerName = player.getName(); int color = 0xFFFFFFFF; if (player.isSneaking()) { // ... } // ... ``` ```java @Inject(method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/Player;isSneaking()Z")) private void changeNameAndColor(CallbackInfo ci, @Local LocalRef name, @Local LocalIntRef color) { if (name.get().equals("LlamaLad7")) { name.set("Herobrine"); color.set(0xFFFF0000); } } ``` ```diff String playerName = player.getName(); int color = 0xFFFFFFFF; + LocalRef ref1 = new LocalRefImpl(playerName); + LocalIntRef ref2 = new LocalIntRefImpl(color); + changeNameAndColor(null, ref1, ref2); + color = ref2.get(); + playerName = ref1.get(); if (player.isSneaking()) { // ... } // ... ``` -------------------------------- ### Code Diff Example for ModifyReturnValue Source: https://github.com/llamalad7/mixinextras/wiki/ModifyReturnValue Illustrates the transformation applied to the target method's bytecode when using @ModifyReturnValue. The original return statement is replaced with a call to the handler method. ```diff - return this.speed * 10f - 0.5f; + return halveSpeed(this.speed * 10f - 0.5f); ``` -------------------------------- ### Conditionally Skip Rendering with @Cancellable Source: https://github.com/llamalad7/mixinextras/wiki/Cancellable Use @Cancellable with CallbackInfo to cancel a target method's execution based on specific conditions. This example cancels rendering if the texture is for a poison heart. ```java public void renderHeart(MatrixStack matrices, HeartType type) { Identifier texture = type.getTexture(); // ... Draw logic using `texture` } ``` ```java @ModifyExpressionValue(method = "renderHeart", at = @At(value = "INVOKE", target = "Lsome/package/HeartType;getTexture()Lsome/package/Identifier;")) private Identifier dontRenderPoisonHearts(Identifier texture, @Cancellable CallbackInfo ci) { if (texture.equals(Textures.POSION_HEART)) { ci.cancel(); } return texture; } ``` -------------------------------- ### Targeting an if condition with @Expression Source: https://github.com/llamalad7/mixinextras/wiki/Expressions Use @Expression to target a specific part of bytecode, such as an if condition. Define custom identifiers with @Definition. This example targets the 'fallDistance > 0.0' check in the 'fall' method. ```java @Definition(id = "fallDistance", field = "Lnet/minecraft/entity/Entity;fallDistance:F") @Expression("this.fallDistance > 0.0") @ModifyExpressionValue(method = "fall", at = @At("MIXINEXTRAS:EXPRESSION")) private boolean yourHandler(boolean original) { return original && MyMod.shouldFall(this); } ``` -------------------------------- ### ModifyReceiver Example for Method Call Source: https://github.com/llamalad7/mixinextras/wiki/ModifyReceiver Use this annotation to intercept and potentially change the receiver object of a non-static method call. The handler method receives the original receiver and call arguments, and returns the new receiver. ```java @ModifyReceiver( method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;setX(I)V") ) private TargetClass changeObject(TargetClass receiver, int newX) { if (newX == 10) { return object2; } return receiver; } ``` -------------------------------- ### Capture Local Variable with @Local Source: https://github.com/llamalad7/mixinextras/wiki/Local Use @Local to capture a local variable for use in your Mixin method. Specify the variable by its type, ordinal position, or LVT index. This example shows capturing a boolean 'bl1' to use in modifying 'bl2'. ```java boolean bl1 = complexThing1() && complexThing2() || complexThing3(); boolean bl2 = !complexThing4() || complexThing5(); ``` ```java @ModifyVariable(method = "targetMethod", at = @At("STORE"), ordinal = 1) private boolean modifybl2ForReasons(boolean original, @Local(ordinal = 0) boolean bl1) { return original && YourMod.anotherComplexThing(bl1); } ``` ```diff boolean bl2 = !complexThing4() || complexThing5(); + bl2 = modifybl2ForReasons(bl2, bl1); ``` -------------------------------- ### MixinExtras Initialization Source: https://github.com/llamalad7/mixinextras/blob/master/README.MD Initialize MixinExtras by calling MixinExtrasBootstrap.init() early in your application, preferably in an IMixinConfigPlugin's onLoad method. ```java MixinExtrasBootstrap.init(); ``` -------------------------------- ### Code Diff for Wrapping Method Source: https://github.com/llamalad7/mixinextras/wiki/WrapMethod Shows the code transformation applied by the WrapMethod injector, demonstrating how the original method is modified to include the wrapper logic. ```diff public int riskyCalculation(String someParam) throws SomeException { return this.guardRiskyCalculation(someParam, args -> { - // Some logic using someParam + // Some logic using args[0] }); } ``` -------------------------------- ### Method Calls in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Supports calling instance and static methods with zero or more arguments. Method identifiers must be defined in a @Definition. ```java x.someMethod() // no arguments x.someMethod(x) // 1 argument x.someMethod(x, y) // 2 arguments ... staticMethod() // no arguments staticMethod(x) // 1 argument ``` -------------------------------- ### Mixin Configuration for MixinExtras Source: https://github.com/llamalad7/mixinextras/wiki/Expressions-Setup Add this entry to your mixin configuration file to enable MixinExtras and specify the minimum required version. ```json { "mixinextras": { "minVersion": "" } } ``` -------------------------------- ### Fabric Dependency with Slim Jar Source: https://github.com/llamalad7/mixinextras/wiki/Expressions-Setup Include the MixinExtras Fabric dependency, optionally using the slim jar for reduced file size. The slim jar is not recommended for development. ```gradle dependencies { implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:")) include("io.github.llamalad7:mixinextras-fabric::slim") } ``` -------------------------------- ### Code Diff for Sharing an Argument Source: https://github.com/llamalad7/mixinextras/wiki/Share This diff shows the changes made to the target method to incorporate the @Share functionality, including the initialization and usage of the LocalIntRef. ```diff public void targetMethod() { + LocalIntRef argRef = new LocalIntRefImpl(0); // ... - this.renderSomething(someCalculation()); + this.renderSomething(saveRenderSomethingArg(someCalculation(), argRef)); // ... + renderCustomThing(new CallbackInfo(), argRef); } ``` -------------------------------- ### MixinExtras Expression Language Syntax Source: https://context7.com/llamalad7/mixinextras/llms.txt This snippet outlines the various components of the MixinExtras Expression Language, including literals, operators, comparisons, wildcards, member access, method calls, instantiation, array operations, casts, instanceof, returns, throws, and explicit target annotations. Use this for understanding the syntax and capabilities. ```java // Literals 'hello' // String 23 // int / long 0xFF // int / long 1.5 // float / double true // boolean (int in bytecode) null ``` ```java // Arithmetic & bitwise binary operators a + b, a - b, a * b, a / b, a % b a & b, a | b, a ^ b, a << b, a >> b, a >>> b ``` ```java // Comparisons (must be top-level in expression) a == b, a != b, a < b, a <= b, a > b, a >= b ``` ```java // Wildcards ? // any expression someObject.? // any field/method on someObject someObject.?(x) // any 1-arg call on someObject ``` ```java // Member access, method calls, instantiation x.someField x.someMethod(arg1, arg2) staticMethod(arg) new SomeType(arg) ``` ```java // Array operations someArray[i] someArray[i] = value ``` ```java // Cast, instanceof (SomeType) expr expr instanceof SomeType ``` ```java // Returns and throws return expr throw expr ``` ```java // Explicit target annotation throw @(new SomeException('msg')) this.method(@(value1), @(value2)) ``` -------------------------------- ### Instantiations in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Enables the creation of new objects using constructors with or without arguments. The type must be defined in a @Definition. ```java new SomeType() // no arguments new SomeType(x, y) // 2 arguments ... ``` -------------------------------- ### Method References, Constructor References, and Lambdas in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Supports unbound and bound method references, constructor references, and lambdas. These must be defined in a @Definition. Capturing 'this' makes a lambda bound; otherwise, it's unbound. Unbound references can match static or non-static methods. ```java ::someMethod // unbound reference someReceiver::someMethod // bound reference SomeType::new // constructor reference ::someLambda // the lambda is considered unbound if it doesn't access `this`, i.e. its implementation is static this::someLambda // if the lambda does access `this`, it is considered bound ``` -------------------------------- ### Code Diff for Cancellable Method Integration Source: https://github.com/llamalad7/mixinextras/wiki/Cancellable This diff shows the changes made to a target method to incorporate the cancellable logic. It includes the creation of CallbackInfo and a check for cancellation before proceeding. ```java public void targetMethod() { + CallbackInfo ci = new CallbackInfo(...); - Identifier texture = type.getTexture(); + Identifier texture = dontRenderPoisonHearts(type.getTexture(), ci); + if (ci.isCancelled()) { + return; + } // ... Draw logic using `texture` } ``` -------------------------------- ### Code Diff for Wrap Operation Source: https://github.com/llamalad7/mixinextras/wiki/WrapOperation Illustrates the code transformation applied by the @WrapOperation, showing how the original method call is replaced with a call to the handler method, which then conditionally invokes the original operation. ```diff - int number = this.expensiveCalculation(flag); + int number = this.bypassExpensiveCalculationIfNecessary(this, flag, args -> { + return ((TargetClass) args[0]).expensiveCalculation((Boolean) args[1]); + }); ``` -------------------------------- ### Binary Expressions in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Supports standard arithmetic, shift, and bitwise binary operations. Precedences align with Java. Logical AND/OR (&&, ||) and NOT (!) are not supported. ```java a * b a / b a % b a + b a - b a << b a >> b a >>> b a & b a ^ b a | b ``` -------------------------------- ### Return and Throw Statements in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Allows for explicit return of a value or throwing of an exception within expressions. ```java return someExpression throw someException ``` -------------------------------- ### Using wildcards for array assignments Source: https://github.com/llamalad7/mixinextras/wiki/Expressions Wildcards (?) can be used to match any value in an array assignment. Defining locals with @Definition is possible but often more brittle than using wildcards. ```java this.pistonMovementDelta[?] = ? ``` -------------------------------- ### Using wildcards for arithmetic expressions Source: https://github.com/llamalad7/mixinextras/wiki/Expressions Wildcards can be used to match operands in arithmetic expressions. Specifying locals is an alternative but can be less flexible. ```java return this.distance < ? * ? ``` -------------------------------- ### Comparison Expressions in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Enables equality, inequality, and relational comparisons. These must be top-level expressions and can be modified. Be cautious with floating-point comparisons due to potential indistinguishability from their inverted counterparts. ```java a == b a != b a < b a <= b a > b a >= b ``` -------------------------------- ### Enable MixinExtras Expression Language in Mixin Config Source: https://context7.com/llamalad7/mixinextras/llms.txt Add this configuration to your .mixins.json to enable the Expression Language. ```json { "mixinextras": { "minVersion": "0.5.4" } } ``` -------------------------------- ### Conditional Code Block Insertion Source: https://github.com/llamalad7/mixinextras/wiki/WrapWithCondition Shows how the original code is wrapped with the conditional check generated by WrapWithCondition. The original method call is only executed if the handler method returns true. ```diff + if (onlyRenderIfAllowed(this, this.tickDelta)) { this.render(this.tickDelta); + } ``` -------------------------------- ### Using wildcards for instanceof checks Source: https://github.com/llamalad7/mixinextras/wiki/Expressions Wildcards can be used to match the object in an instanceof check. Similar to method calls, locals can be defined but are often less robust than using wildcards. ```java ? instanceof ServerPlayerEntity ``` -------------------------------- ### Injecting after a method call with @Expression Source: https://github.com/llamalad7/mixinextras/wiki/Expressions Use @Expression with a wildcard (?) to match specific arguments in a method call. Wildcards can also be used for method names. @Definition can be used to define methods and fields for use in expressions. ```java this.emitGameEvent(GameEvent.ENTITY_MOUNT, passenger); ``` ```java @Definition(id = "emitGameEvent", method = "Lnet/minecraft/entity/Entity;emitGameEvent(Lnet/minecraft/world/event/GameEvent;Lnet/minecraft/entity/Entity;)V") @Definition(id = "ENTITY_MOUNT", field = "Lnet/minecraft/world/event/GameEvent;ENTITY_MOUNT:Lnet/minecraft/world/event/GameEvent;") @Expression("this.emitGameEvent(ENTITY_MOUNT, ?)") @Inject(method = "addPassenger", at = @At(value = "MIXINEXTRAS:EXPRESSION", shift = At.Shift.AFTER)) private void yourHandler(CallbackInfo ci) { System.out.println("Hi!"); } ``` -------------------------------- ### Inject After Call with Wildcards using @Expression and @Inject Source: https://context7.com/llamalad7/mixinextras/llms.txt Employ @Expression with @Inject to precisely target a method call, even with wildcards for arguments. This enables injecting code after a specific call, like emitting a game event with a wildcard for the passenger entity. ```java @Definition(id = "emitGameEvent", method = "Lnet/minecraft/entity/Entity;emitGameEvent(Lnet/minecraft/world/event/GameEvent;Lnet/minecraft/entity/Entity;)V") @Definition(id = "ENTITY_MOUNT", field = "Lnet/minecraft/world/event/GameEvent;ENTITY_MOUNT:Lnet/minecraft/world/event/GameEvent;") @Expression("this.emitGameEvent(ENTITY_MOUNT, ?)") @Inject(method = "addPassenger", at = @At(value = "MIXINEXTRAS:EXPRESSION", shift = At.Shift.AFTER)) private void yourHandler(CallbackInfo ci) { System.out.println("Passenger mounted!"); } ``` -------------------------------- ### Array Creation in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Allows for the creation of arrays, including filled, empty, and multi-dimensional arrays. The array type must be defined in a @Definition. ```java new SomeType[]{someValue, someOtherValue, aThirdValue} // filled array creation new SomeType[someLength] // empty array creation new SomeType[3][4][5] // multi-dimensional array creation ``` -------------------------------- ### Array Operations in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Enables loading from and storing into array elements using an index. These operations have special support within @WrapOperation. ```java someArray[someIndex] // load someArray[someIndex] = someValue // store ``` -------------------------------- ### Capture Local Variables with @Local Source: https://context7.com/llamalad7/mixinextras/llms.txt Captures local variables within the target method at the injection point. Supports read-only access or mutable reference capture for objects and primitive types. ```java // Read-only capture — access bl1 while modifying bl2: @ModifyVariable(method = "targetMethod", at = @At("STORE"), ordinal = 1) private boolean modifyBl2(boolean original, @Local(ordinal = 0) boolean bl1) { return original && YourMod.anotherComplexThing(bl1); } ``` ```java // Mutable reference capture — modify both playerName and color simultaneously: @Inject(method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/Player;isSneaking()Z")) private void changeNameAndColor(CallbackInfo ci, @Local LocalRef playerName, @Local LocalIntRef color) { if (playerName.get().equals("LlamaLad7")) { playerName.set("Herobrine"); color.set(0xFFFF0000); } } ``` ```java // Capturing by LVT index: @Inject(method = "targetMethod", at = @At("TAIL")) private void logSpecificLocal(CallbackInfo ci, @Local(index = 3) int specificVar) { YourMod.log("Value at LVT 3: " + specificVar); } ``` -------------------------------- ### Wrap Method Call with Condition Source: https://github.com/llamalad7/mixinextras/wiki/WrapWithCondition Wraps a method call with a conditional check. The handler method should return true to allow the original call to proceed. Multiple mods can apply conditions, and all will be checked. ```java @WrapWithCondition( method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;render(F)V") ) private boolean onlyRenderIfAllowed(TargetClass instance, float tickDelta) { return YourMod.shouldRender(); } ``` -------------------------------- ### Using wildcards for method calls with string literals Source: https://github.com/llamalad7/mixinextras/wiki/Expressions Wildcards can be used in method calls, and string literals within expressions should use single quotes. A wildcard can also be used to match any method call with no arguments. ```java ?.putShort('Fire', ?) ``` -------------------------------- ### Share Values Between Handler Methods with @Share Source: https://github.com/llamalad7/mixinextras/wiki/Share Use @Share to pass values between handler methods. The parameter type must be from the LocalRef family, and the ID in the annotation is used to match references. IDs are per-mixin by default. ```java @Share("fov") LocalDoubleRef fov ``` ```java @Share("pos") LocalRef pos ``` ```java @Share("x") LocalIntRef x ``` -------------------------------- ### Share Values Between Handlers with @Share Source: https://context7.com/llamalad7/mixinextras/llms.txt Enables sharing values between different handler methods within the same target method invocation using `LocalRef` objects. Useful for inter-handler communication and can be configured with a namespace for cross-mixin sharing. ```java // Save an argument in one handler, read it in another: @ModifyArg( method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;renderSomething(I)V") ) private int saveRenderArg(int arg, @Share("arg") LocalIntRef argRef) { argRef.set(arg); return arg; // pass through unchanged } @Inject(method = "targetMethod", at = @At("TAIL")) private void renderCustomThing(CallbackInfo ci, @Share("arg") LocalIntRef argRef) { this.renderSomethingCustom(argRef.get()); // use the previously saved value } ``` ```java // Cross-mixin sharing (explicit namespace): @Inject(method = "tick", at = @At("HEAD")) private void captureTickStart(CallbackInfo ci, @Share(value = "tickStart", namespace = "yourmodid") LocalLongRef ref) { ref.set(System.nanoTime()); } ``` -------------------------------- ### Identifier Expressions in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Allows loading local variables or static fields, and storing values into them. Identifiers must be defined in a @Definition. ```java someLocal // load someLocal = someValue // store SOME_STATIC_FIELD // get SOME_STATIC_FIELD = someValue // put ``` -------------------------------- ### Explicitly Targeting an Expression Source: https://github.com/llamalad7/mixinextras/wiki/Expressions Use the `@(...)` syntax to explicitly target a specific part of an expression, such as the instantiation of an object, allowing for modification before the final operation. ```java throw @(new IllegalStateException('Oh no!')) ``` -------------------------------- ### Wrap Entire Methods with @WrapMethod Source: https://context7.com/llamalad7/mixinextras/llms.txt Use @WrapMethod to wrap the entire target method body. The handler receives original parameters and an Operation. Useful for try/catch guards, synchronization, or logic at the start/end of a method. ```java // Target method: public int riskyCalculation(String someParam) throws SomeException { ... } // Goal: return a safe default instead of propagating the exception. @WrapMethod(method = "riskyCalculation") private int guardRiskyCalculation(String someParam, Operation original) { try { return original.call(someParam); } catch (SomeException ignored) { return 0; } } ``` ```java // Synchronizing a method: @WrapMethod(method = "updateState") private void synchronizedUpdate(Object param, Operation original) { synchronized (this) { original.call(param); } } ``` ```java // Sharing values between a WrapMethod and inner injectors via @Share: @WrapMethod(method = "targetMethod") private void wrapWithTimer(Operation original, @Share("startTime") LocalLongRef startTimeRef) { startTimeRef.set(System.currentTimeMillis()); original.call(); long elapsed = System.currentTimeMillis() - startTimeRef.get(); YourMod.recordTiming(elapsed); } ``` -------------------------------- ### Conditionally Gate Void Calls or Field Writes with @WrapWithCondition Source: https://context7.com/llamalad7/mixinextras/llms.txt Use @WrapWithCondition to gate void method calls or field writes. The operation is skipped if the handler returns false. Multiple conditions must all return true to proceed. ```java // Target code: this.render(this.tickDelta); // Goal: skip rendering unless your mod allows it. @WrapWithCondition( method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;render(F)V") ) private boolean onlyRenderIfAllowed(TargetClass instance, float tickDelta) { return YourMod.shouldRender(); } ``` ```java // Guarding a field write — prevent writing if the new value is invalid: @WrapWithCondition( method = "targetMethod", at = @At(value = "FIELD", target = "Lsome/package/TargetClass;name:Ljava/lang/String;", opcode = Opcodes.PUTFIELD) ) private boolean preventNullName(TargetClass instance, String newName) { return newName != null && !newName.isEmpty(); } ``` -------------------------------- ### Unary Expressions in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Handles unary negation and bitwise NOT operations. Note that bitwise NOT is indistinguishable from XORing with -1. ```java -x ~x ``` -------------------------------- ### Wildcard Expressions in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Wildcards ('?') can replace entire expressions or identifiers, offering brevity or matching elements that cannot be explicitly specified, such as those involving jumps. ```java ? someObject.? someObject.?(someArg) ``` -------------------------------- ### Implicit Targeting of Entire Expression Source: https://github.com/llamalad7/mixinextras/wiki/Expressions If no explicit targets are defined, the entire expression is implicitly targeted. This is equivalent to wrapping the whole expression in `@(...)`. ```java this.myMethod(5) ``` ```java @(this.myMethod(5)) ``` -------------------------------- ### Guard Field Write with @WrapOperation Source: https://context7.com/llamalad7/mixinextras/llms.txt Wrap a field write operation to add validation logic before allowing the write. The original operation is only called if validation passes. ```java // Wrapping a field write — only allow writing if validation passes: @WrapOperation( method = "targetMethod", at = @At(value = "FIELD", target = "Lsome/package/TargetClass;health:I", opcode = Opcodes.PUTFIELD) ) private void guardHealthWrite(TargetClass instance, int newHealth, Operation original) { if (newHealth >= 0) { original.call(instance, newHealth); } } ``` -------------------------------- ### Literals in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Supports various literal types including strings, integers, floats, booleans, and null. Be mindful of boolean literals potentially being treated as integers. ```java 'hello' // String 'x' // String or char 23 // int or long 0xFF // int or long 1.5 // float or double true // boolean (or int, be careful) null ``` -------------------------------- ### Member Expressions in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Facilitates accessing fields of objects or arrays. The '.length' member for arrays is a built-in shortcut. Field identifiers must be defined in a @Definition. ```java x.someField // get x.someField = someValue // put ``` -------------------------------- ### Default Expression Targeting Source: https://github.com/llamalad7/mixinextras/wiki/Expressions By default, Mixin targets the last instruction in an expression. This is the standard behavior when no explicit target is specified. ```java throw new IllegalStateException("Oh no!"); ``` -------------------------------- ### Instanceof Checks in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Provides the 'instanceof' operator to check if an expression is an instance of a specified type. The type must be defined in a @Definition. ```java x instanceof SomeType ``` -------------------------------- ### Casts in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Supports casting expressions to a specified type. The target type must be defined in a @Definition. Primitive casts are built-in. ```java (SomeType) someExpression ``` -------------------------------- ### Cancel Method Execution with @Cancellable Source: https://context7.com/llamalad7/mixinextras/llms.txt Use @Cancellable with CallbackInfo or CallbackInfoReturnable to cancel a target method's execution from any injector type. This is useful for preventing unwanted method behavior, such as rendering specific items or computing damage in god mode. ```java private Identifier dontRenderPoisonHearts(Identifier texture, @Cancellable CallbackInfo ci) { if (texture.equals(Textures.POISON_HEART)) { ci.cancel(); // renderHeart returns early } return texture; } ``` ```java private int interceptDamage(Entity instance, Operation original, @Cancellable CallbackInfoReturnable cir) { if (YourMod.isGodMode(instance)) { cir.setReturnValue(0f); // cancel computeDamage and return 0 } return original.call(instance); } ``` -------------------------------- ### Target Expressions in Expression Language Source: https://github.com/llamalad7/mixinextras/wiki/Expression-Language Uses '@(expression)' syntax to explicitly target specific parts of an expression for modification by an injector. If no target is explicit, the entire expression is implicitly targeted. ```java @(someExpression) // E.g.: someMethod(@(new SomeType())) this.something + @(this.somethingElse) this.someMethod(@(value1), @(value2)) ``` -------------------------------- ### Adjust Method Return Values with @ModifyReturnValue Source: https://context7.com/llamalad7/mixinextras/llms.txt Intercept and replace the value just before it is returned from the target method. This chains when multiple mods apply it. Accepts RETURN, TAIL, and MIXINEXTRAS:EXPRESSION. ```java // Target code: return this.speed * 10f - 0.5f; // Goal: halve whatever the method returns. @ModifyReturnValue( method = "targetMethod", at = @At("RETURN") ) private float halveSpeed(float original) { return original / 2f; } ``` ```java // Appending a suffix to every returned string: @ModifyReturnValue( method = "getDisplayName", at = @At("RETURN") ) private String appendModTag(String original) { return original + " [YourMod]"; } ``` ```java // Only modifying the final return (TAIL): @ModifyReturnValue( method = "computeScore", at = @At("TAIL") ) private int applyBonus(int original) { return original + YourMod.getBonusPoints(); } ``` -------------------------------- ### Extend Instanceof Check with @WrapOperation Source: https://context7.com/llamalad7/mixinextras/llms.txt Wrap an instanceof check to include additional types in the condition. The original check is performed, and the result is combined with a check for custom types. ```java // Wrapping an instanceof check — extend which types are accepted: @WrapOperation( method = "targetMethod", at = @At(value = "INVOKE", target = "...") // use appropriate injection point ) private boolean extendInstanceofCheck(Object obj, Operation original) { return original.call(obj) || obj instanceof YourCustomType; } ``` -------------------------------- ### Modifying expression values with @Expression and @Definition Source: https://github.com/llamalad7/mixinextras/wiki/Expressions Target instantiations or specific types using @Definition with the 'type' parameter. Use @Local to reference local variables within the target method. Wildcards can be used for locals if their exact identity is not critical. ```java new BlockStateParticleEffect(ParticleTypes.BLOCK, blockState) ``` ```java @Definition(id = "BlockStateParticleEffect", type = BlockStateParticleEffect.class) @Definition(id = "BLOCK", field = "Lnet/minecraft/particle/ParticleTypes;BLOCK:Lnet/minecraft/particle/ParticleType;") @Definition(id = "blockState", local = @Local(type = BlockState.class)) @Expression("new BlockStateParticleEffect(BLOCK, blockState)") @ModifyExpressionValue(method = "spawnSprintingParticles", at = @At("MIXINEXTRAS:EXPRESSION")) private BlockStateParticleEffect yourHandler(BlockStateParticleEffect original) { return YourMod.processParticle(original); } ``` -------------------------------- ### Target Sub-expression with @Expression Source: https://context7.com/llamalad7/mixinextras/llms.txt Target a sub-expression within the bytecode using @Expression, allowing modification of elements like exceptions before they are thrown. This enables handling or altering exceptions dynamically. ```java @Definition(id = "IllegalStateException", type = IllegalStateException.class) @Expression("throw @(new IllegalStateException('Oh no!'))") @ModifyExpressionValue(method = "riskyMethod", at = @At("MIXINEXTRAS:EXPRESSION")) private IllegalStateException swapException(IllegalStateException original) { return new IllegalStateException("Handled: " + original.getMessage()); } ``` -------------------------------- ### Bypass Expensive Calculation with @WrapOperation Source: https://context7.com/llamalad7/mixinextras/llms.txt Wrap a method call to conditionally bypass its execution. The original operation can be skipped or passed to the next wrapper in the chain. ```java // Target code: int number = this.expensiveCalculation(flag); // Goal: bypass the call when a mod setting is enabled. @WrapOperation( method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;expensiveCalculation(Z)I") ) private int bypassExpensiveCalculationIfNecessary( TargetClass instance, boolean flag, Operation original) { if (YourMod.bypassExpensiveCalculation) { return 10; // skip the original call entirely } return original.call(instance, flag); // call the original (or the next wrapper in the chain) } ``` -------------------------------- ### Target Instantiation with @Expression and @ModifyExpressionValue Source: https://context7.com/llamalad7/mixinextras/llms.txt Use @Expression with @ModifyExpressionValue to target a specific object instantiation. This allows modifying the created object, such as processing a BlockStateParticleEffect before it's used. ```java @Definition(id = "BlockStateParticleEffect", type = BlockStateParticleEffect.class) @Definition(id = "BLOCK", field = "Lnet/minecraft/particle/ParticleTypes;BLOCK:Lnet/minecraft/particle/ParticleType;") @Definition(id = "blockState", local = @Local(type = BlockState.class)) @Expression("new BlockStateParticleEffect(BLOCK, blockState)") @ModifyExpressionValue(method = "spawnSprintingParticles", at = @At("MIXINEXTRAS:EXPRESSION")) private BlockStateParticleEffect yourHandler(BlockStateParticleEffect original) { return YourMod.processParticle(original); } ``` -------------------------------- ### Modifying Method Arguments with @ModifyArg Source: https://github.com/llamalad7/mixinextras/wiki/Expressions When modifying arguments of a method call, it is recommended to use `@ModifyArg` with a specific target like `this.setX(?)` instead of attempting to target the entire expression with `@ModifyExpressionValue`. ```java this.setX(...); ``` -------------------------------- ### Modify Return Value with @ModifyReturnValue Source: https://github.com/llamalad7/mixinextras/wiki/ModifyReturnValue Use this annotation to intercept and alter the value returned by a method. The handler method receives the original return value and must return the modified value. It supports injection points like RETURN, TAIL, and MIXINEXTRAS:EXPRESSION. ```java import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyReturnValue; public class ExampleMixin { @ModifyReturnValue( method = "targetMethod", at = @At("RETURN") ) private float halveSpeed(float original) { return original / 2f; } } ``` -------------------------------- ### Modify Expression Value with INVOKE Injection Source: https://github.com/llamalad7/mixinextras/wiki/ModifyExpressionValue Use this to alter the boolean result of a method call. The handler receives the original boolean value and returns the modified one. This is useful for adding conditions to existing logic. ```java @ModifyExpressionValue( method = "targetMethod", at = @At(value = "INVOKE", target = "Ltarget/Class;shouldFly()Z") ) private boolean onlyFlyIfAllowed(boolean original) { return original && YourMod.isFlyingAllowed(); } ``` -------------------------------- ### Tweak Expression Results with @ModifyExpressionValue Source: https://context7.com/llamalad7/mixinextras/llms.txt Intercept and modify the resulting value of an expression (method call, field read, constant, etc.). This chains when multiple mods target the same expression. Accepts INVOKE, FIELD, CONSTANT, NEW, and MIXINEXTRAS:EXPRESSION. ```java // Target code: if (this.shouldFly()) { this.fly(); } // Goal: add an extra condition on top. @ModifyExpressionValue( method = "targetMethod", at = @At(value = "INVOKE", target = "Ltarget/Class;shouldFly()Z") ) private boolean onlyFlyIfAllowed(boolean original) { return original && YourMod.isFlyingAllowed(); } ``` ```java // Capping a returned integer value from a method call: @ModifyExpressionValue( method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/Class;getMaxHealth()I") ) private int capMaxHealth(int original) { return Math.min(original, YourMod.hardCap()); } ``` ```java // Replacing a constant: @ModifyExpressionValue( method = "targetMethod", at = @At(value = "CONSTANT", args = "intValue=20") ) private int changeTickRate(int original) { return YourMod.customTickRate(); } ``` -------------------------------- ### Modify Receiver with @ModifyReceiver Source: https://context7.com/llamalad7/mixinextras/llms.txt Intercepts and potentially changes the receiver object for non-static method calls or field accesses. Useful for redirecting operations to different instances based on conditions. ```java // Target code: object1.setX(newXPosition); // Goal: redirect the call to object2 under certain conditions. @ModifyReceiver( method = "targetMethod", at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;setX(I)V") ) private TargetClass changeObject(TargetClass receiver, int newX) { if (newX == 10) { return object2; } return receiver; } ``` ```java // Redirecting a field read to a different instance: @ModifyReceiver(method = "targetMethod", at = @At(value = "FIELD", target = "Lsome/package/TargetClass;health:I", opcode = Opcodes.GETFIELD) ) private TargetClass readHealthFromProxy(TargetClass receiver) { return YourMod.getProxyFor(receiver); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.