NativeMethodAccess - friendlyhj/ZenUtils GitHub Wiki
@since 1.19.0
A native method is a method whose implementation is provided by other programming languages.
You can directly access java native classes and methods in zenscript.
Besides the name of the native class, native.
prefix is required.
import native.net.minecraft.world.World;
to import net.minecraft.world.World
class.
All public methods and fields are accessible by zenscript.
CraftTweaker has tons of classes, like IWorld
, IPlayer
and so on, which are basically wrappers of Minecraft classes. Native Minecraft classes are required for native methods, but in crafttweaker events and other zenscript api, only CraftTweaker wrappers are given. Thus, conversion between each other is necessary.
For (almost all) CraftTweaker classes, call native
getter to convert it to mc classes.
For mc classes with the corresponding CraftTweaker class, call wrapper
getter to convert it to CraftTweaker classes.
The conversion also be applied as caster, so as XXX
is available, and conversion will be automatic when passing in a function or method.
It is widely known that Minecraft is a commercial game, its code is obfuscated. MCP provides a mapping between obfuscated and human-readable names to ease the development of mods. ZenUtils downloads MCP version stable-39
to remap methods. Then MCP names are available in zenscript, ZenUtils translates them to mc obfuscated names to access mc methods.
Downloading may take a little while. By default, downloading will start and blocks the game loading until finished when you use native method by the first time. If you are certain you will use native methods, you can add #download_reobf_mapping
preprocessor to start to download asynchronously when preinit.
About getFoo()
and setFoo(foo)
java getter/setter method, zenscript foo
getter/setter can be translated to them.
For a java type Iterable<T>
or any subclasses, you can iterate it, get its length and cast it to list.
Example:
val set = foo(); // foo returns Set<String>, Set is a subclass of Iterable.
val list = set as [string]; // casts it to string list
print(set.length); // gets its length
for s in set { // iterate it, the type of s variable is string
print("a string:" ~ s);
}
==
!=
operator are available to all native classes, it calls Objects.equals
java method.
Left unchecked cast is allowed to the crt class. But the native class is not, and requires right checked cast.
// crt
val entity as IEntity = event.entity;
if (entity instanceof IPlayer) {
val player as IPlayer = entity;
}
// mc
val mcEntity as Entity = entity.native;
if (mcEntity instanceof Player) {
val mcPlayer = entity as Player;
}
import crafttweaker.event.PlayerLoggedInEvent;
import crafttweaker.player.IPlayer;
import native.net.minecraft.world.gen.ChunkProviderServer;
import native.net.minecraft.world.chunk.Chunk;
import native.net.minecraft.world.World;
// expansion method, syntax:
// https://docs.blamejared.com/1.12/en/AdvancedFunctions/Expansion_Methods
$expand World$loadedChunkCount() as int {
val chunkProvider = this.chunkProvider;
if (chunkProvider instanceof ChunkProviderServer) {
// loadedChunks getter calls getLoadedChunks() method and returns Set<Chunk>
return (chunkProvider as ChunkProviderServer).loadedChunks.length;
}
return 0;
}
events.onPlayerLoggedIn(function(event as PlayerLoggedInEvent) {
val player = event.player;
if (event.player.world.remote) return;
event.player.world.catenation()
.sleep(10)
.run(function(world, context) {
print(world.native.loadedChunkCount() ~ " chunks loaded");
})
.start();
});
Not all native codes are exposed to zenscript, sensitive operations are blacklisted from being called or referenced.
It includes:
- File IO
- Network
- Window
- Multithreading
- Mixin/ASM
- Other language lib (scala kotlin groovy)
- Java low-level code (
java.lang
,jdk.
,sun.
, ...)
@since 1.22.4
java.lang.Class
is the core of Java Reflection API, it should be forbidden generally speaking. However, many mod codes also use it. In order to call these codes, ZenUtils allows to access it with any reflection permission denied.
import native.java.lang.Class; // import
import native.java.util.ArrayList;
import native.net.minecraft.item.ItemStack;
// call `class` static getter to get a class
val listClass = ArrayList.class;
// prints "class java.util.ArrayList"
print(toString(listClass));
val item as ItemStack = <minecraft:apple>.native;
// gets the runtime class of an object
// these two are equivalent
// only native objects are supported
// if the object is a forbidden one, the getter will return its unbanned nearest parent class
val itemClass = item.class;
val itemClass2 = item.getClass();
// all members of Class are forbidden, you can only pass Class object to mod code
print(itemClass.getName()); // error
val methods = itemClass.getMethods(); // error
// of course, you can not get a class from a string
Class.forName("java.io.InputStream"); // error