-
Notifications
You must be signed in to change notification settings - Fork 10
Mixin
@since 1.20.0
Mixin is a framework to hook into runtime class loading to modify java code. ZenUtils allows to use mixin by zenscript.
The feature is disabled by default. You should enable it in config first.
All mixin scripts are loaded by loader mixin
. (i.e. #loader mixin
) They are not reloadable certainly. Mixin relies a class and annotations. So we need to write zenClass and annotation mechanism. ZenUtils adds a preprocessor to resolve annotation.
Example:
#loader mixin
#mixin Mixin
#{targets: "zmaster587.libVulpes.tile.energy.TilePlugBase"}
zenClass MixinTilePlugBase {
#mixin ModifyConstant
#{
# method: "getMaxEnergy",
# constant: {intValue: 10000}
#}
function modifyMaxEnergyModifier(value as int) as int {
return 20000000;
}
}
The #mixin NAME
is the header of mixin preprocessor, to define which annotation. A json after the header line describes the annotation contents (can be omitted). It still is considered as a preprocessor, so #
is still required to be put at the start of every line. Then we write a function or a field after the json. There cannot be an empty line between the three parts.
The example is translated to such java code.
@Mixin(targets = "zmaster587.libVulpes.tile.energy.TilePlugBase")
public class MixinTilePlugBase {
@ModifyConstant(method = "getMaxEnergy", constant = @Constant(intValue = 10000))
private int modifyMaxEnergyModifier(int value) {
return 20000000;
}
}
Some functions are required to be static by mixin. But zenscript doesn't allow static functions in zenClass. Add #mixin Static
to mark the function is static.
Example:
#loader mixin
#mixin Mixin
#{targets: "blusunrize.immersiveengineering.common.blocks.metal.TileEntitySilo"}
zenClass MixinTileEntitySilo {
#mixin Static
#mixin ModifyConstant
#{
# method: "<clinit>",
# constant: {intValue: 41472}
#}
function buffStorage(value as int) as int {
return 0xffff00;
}
}
CallbackInfo
and CallbackInfoReturnable
is required in Inject hooks. Use mixin.CallbackInfo
and mixin.CallbackInfoReturnable
to import them. CallbackInfoReturnable
has a generic type. But Zenscript doesn't support it. So CallbackInfoReturnable#getReturnValue
always returns java.lang.Object
in zenscript, you must cast it later.
In modpack environment, the game always be obfuscated. Mixin remap system is unnecessary. ZenUtils hardcodes mixin annotation to remap=false
. So srg names are required to mixin inject points, mcp names are not allowed. But you still can use mcp name in function body to call mc code.
In mixin scripts, only native types are allowed, because mixin scripts are loaded before CraftTweaker registers its types.
In mixin of java code, we use ((TargetClass) (Object) this)
to cast this
to target class. In zenscript, if there is only one target class, you use just call this0
variable, it is already casted to target class. You can also use it to call (public) functions and fields of the super class.
Where is the mixin config json? No, zenutils loads mixin classes dynamically. You needn't define mixin class name in mixin config. If the target class is client only, add #sideonly client
preprocessor to make the mixin script is only loaded on client.
You can view more examples in this PR.
- GlobalFunctions
- ScriptReloading
- SuppressErrorPreprocessor
- HardFailPreprocessor
- OrderlyMap
- IData Deep Update
- Template String
- Native Method Access
- Mixin
- CrTI18n
- CrTUUID
- CrTItemHandler
- CrTLiquidHandler
- ILiquidTankProperties
- StringList
- HexHelper
- StaticString
- Catenation
- PersistedCatenation
- PlayerStat
- IStatFormatter
- GameRuleHelper
- ZenCommand
- ZenCommandTree
- ZenUtilsCommandSender
- IGetCommandUsage
- ICommandExecute
- IGetTabCompletion
- CommandUtils