Lua minecraft modding API?

This might be the stupidest idea of all time, but I’ve been thinking about the possibility of a modding API where mods are written in Lua instead of Java. Common sense says it wouldn’t be as powerful as Java, but I question that: native Java objects and methods can be called and created from Lua using reflection, and ASM can create new Java classes/subclasses at runtime (and thus from lua), so what can Java mods do that Lua mods inherently can’t?

The benefit is that mods could theoretically be agnostic to version and modloader if the API is kept stable and ported to each version/modloader tuple. Mods wouldn’t have to be ported individually to new Minecraft versions. Imagine how cool it’d be to write a mod and have it work on 1.7.10, 1.12.2, and whatever the latest version is. I don’t see why that’d be impossible. Of course it’d take a lot of effort to implement the functionality and wrap it in Lua functions; but maybe less effort than has been collectively spent porting mods to the zillions of new Minecraft versions and abandoning old ones? Minimal Java and Gradle involved in development. Hopefully more simplicity. Mods could be sandboxed.

As for speed, there’s an unfortunate overhead to each Java <=> native invocation. I measured calling a Lua function from Java to cost about 100 nanoseconds. But LuaJIT is very fast and shouldn’t be much slower than comparable Java code. I guess the other main drawback is that the API and all functionality would have to be reimplemented from scratch.

I do have a VERY basic proof-of-concept for 1.20.1 Forge:

--no lua highlighting options :(

vani.registerblock(
	localized("cloud_block", "Cloud Block"),
	vani.blockbuilder()
		.hardness(4)
		.texture("clouds.png")
)

vani.registerblock(
	localized("pattern_block", "Pattern Block"),
	vani.blockbuilder()
		.texture("pattern.png")
)

local textures = {
	"storm.png",
	"traffic_light.png",
	"tool_folder.png",
	"beaker.png"
}

for i, file in ipairs(textures) do
	vani.registeritem(
		localized("random_item_" .. i, "Goofy " .. file
			:gsub("%.png", "")
			:gsub("_", " ")
		),
		vani.itembuilder()
			.stacksize(16)
			.texture(file)
	)
end

It works:

The Lua “mod” is just a folder with the code put in in lua/main.lua and the textures inside /static, no JSON or mod-specific build.gradle or anything else. There is a build script written in Lua that takes the mod and outputs a forge 1.20.1 .jar containing the Lua code, resources, and some boilerplate to register the mod. It depends on a library mod that contains the LuaJIT runtime (the binaries are ~3 MB so would be wasteful to include it in every Lua mod) and runs the code.

Most of the work is done by the build step, so far: it sets up all the JSON and langfile stuff. Some blockbuilder() methods do nothing at runtime, like .texture(). Some of them do nothing at build time, like .hardness() and .stacksize(). It’s weird but should hopefully be flexible enough to support any Minecraft version and modloader: maybe in an old version you have to specify the texture of a block at runtime instead of as a resource pack, so in those cases .texture() can do stuff at runtime, and no changes to the mod are required.

It’s a super basic example, yes, but I assume adding more functionality, like block entities and stuff, is possible and is mostly a matter of just… implementing it.

  • Problem: textures and whatnot are mainly defined by resource packs, which are JSON and not Lua, and can’t be created at runtime
    • Solution: the build step. Data generation might also work but it seems more annoying.
  • Problem: minecraft methods are obfuscated, so you can’t call them via reflection at runtime
    • Solution: bundle an Intermediary → SRG name lookup table in the library. Prefer Intermediary names over MCP names due to licensing.
  • Problem: how is a modded block supposed to have its own class or annotations?
    • Solution: ASM.

Sorry this post is too long. I’m obviously not a minecraft modder, so I’m really not knowledgeable about this stuff. I’m way too unmotivated to work on it anymore, but I want to at least hear others’ thoughts on it.

4 Likes

If I know right, Lua is used for modding in different games because of safety(that Java mods don’t have), +it’s sometimes easier to implement than current language game is written in. The idea of crossversion mods is cool, but maybe because of Minecraft api itself it won’t be possible(for some special features, like gt6 ones, just blocks or anything similar can be done on Lua mod loader side)

2 Likes

Yeah, embedding a language to use just for extending mods is a decent idea. KubeJS seems to be the most common for that, you can write addons for mods and register new blocks using Javascript and it is somewhat crossversion but isn’t powerful enough to write a whole mod in (yet?).

I’m not sure yet how my idea differs from KubeJS, only that it’s Lua instead of Javascript.

Exactly what I’m trying to figure out. Modloader or Minecraft APIs being unstable shouldn’t be a problem as long as the Lua API can keep providing a stable API built on top of the unstable ones, but maybe that isn’t possible, I don’t know. The tricky part is that as Minecraft keeps moving towards datapacks, more behavior has to be defined through JSON in the .jar instead of by code at runtime. Worldgen and biomes seem the most affected.

I’ll try to keep working on it until I run into something that makes it impossible. I believe block entities are possible, like one that calls a Lua function every tick. But I have no clue how to even begin to approach stuff like mobs, GUIs, worldgen, biomes, and dimensions… lol. Your reply should give me a few hours of motivation.

Yarn* not Intermediary: GitHub - FabricMC/yarn: Libre Minecraft mappings, free to use for everyone. No exceptions..

Sidenote, Yarn seems only for 1.11 and above, and I didn’t have much luck with the 1.12.2 version. Supporting versions <1.14 might be difficult if I’d have to backport Yarn mappings or something, but that shouldn’t be impossible.

2 Likes

I think tile entities are possible 100%, craft tweaker addons have them, I used them in one of my packs to my custom large crucible

2 Likes

I’ve always wanted to see a Minecraft clone in Common LISP take off. The hot code updates, introspection, MOP/CLOS (object system) and more make it seem a modding paradise.

3 Likes