-
Notifications
You must be signed in to change notification settings - Fork 18
WrapOperation
Allows you to wrap a method call, field get/set, instanceof
check, or object instantiation.
Your handler method receives the targeted instruction's arguments and an Operation
representing the operation being wrapped (optionally followed by the enclosing method's parameters). You should return the same type as the wrapped operation does.
Should be used in favour of Redirect
s when you are wrapping the original operation and not replacing it outright, as unlike Redirect
s, this chains when used by multiple people.
Targeted operation | Handler signature |
---|---|
Non-static method call | private ReturnType yourHandlerMethod(OwnerType instance, <arguments of the original call>, Operation<ReturnType> original) |
super. method call |
private ReturnType yourHandlerMethod(ThisClass instance, <arguments of the original call>, Operation<ReturnType> original) |
Static method call | private ReturnType yourHandlerMethod(<arguments of the original call>, Operation<ReturnType> original) |
Non-static field get | private FieldType yourHandlerMethod(OwnerType instance, Operation<FieldType> original) |
Static field get | private FieldType yourHandlerMethod(Operation<FieldType> original) |
Non-static field write | private void yourHandlerMethod(OwnerType instance, FieldType newValue, Operation<Void> original) |
Static field write | private void yourHandlerMethod(FieldType newValue, Operation<Void> original) |
instanceof check |
private boolean yourHandlerMethod(Object obj, Operation<Boolean> original) |
Object instantiation | private ObjectType yourHandlerMethod(<arguments of the relevant constructor>, Operation<ObjectType> original) |
In all of these cases, you can optionally add the enclosing method's parameters to the end, should you require them for additional context.
When call
ing the original
, you must pass everything before the original
in your handler's parameters. You can optionally pass different values to change what the original
uses.
When targeting code such as the following:
int number = this.expensiveCalculation(flag);
you may wish to wrap the call such that it is bypassed if a setting is enabled.
This could be done like so:
@WrapOperation(
method = "targetMethod",
at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;expensiveCalculation(Z)I")
)
private int bypassExpensiveCalculationIfNecessary(TargetClass instance, boolean flag, Operation<Integer> original) {
if (YourMod.bypassExpensiveCalculation) {
return 10;
} else {
return original.call(instance, flag);
}
}
expensiveCalculation
would then only be called if you called it yourself via the original.call(...)
invocation.
Multiple mods can do this at the same time, and the wrapping will chain. The first to be applied receives an Operation
representing the vanilla call, if another is applied it receives an Operation
representing the first one's wrapping, etc.
- int number = this.expensiveCalculation(flag);
+ int number = this.bypassExpensiveCalculationIfNecessary(this, flag, args -> {
+ return ((TargetClass) args[0]).expensiveCalculation((Boolean) args[1]);
+ });