Skip to content

Changelog

0.9.0 — Removed ByteBuddy, 6 new events (23 total)

Section titled “0.9.0 — Removed ByteBuddy, 6 new events (23 total)”
EventCancellableArgsFires
entity_spawnentityAny entity loads into a world
entity_despawnentityAny entity unloads from a world
entity_deathentity, source, amountAny living entity is about to die (players + mobs)
player_join_initplayerEarly join, player not fully loaded (renamed from player_join)
player_joinplayerPost-load join, player fully ready in world
player_respawnplayer, wasDeathPlayer respawns after death or end portal

Migration: player_join was renamed to player_join_init (still cancellable, early). The new player_join fires later when the player is fully in-world.

Removed: mc.observeHook / mc.removeHook / mc.clearHooks

Section titled “Removed: mc.observeHook / mc.removeHook / mc.clearHooks”

ByteBuddy (byte-buddy + byte-buddy-agent) was pulling ~4.3 MB into the output jar for a single unstable API (mc.observeHook). Use mc.on() events instead.

  • Deleted LuaMixinManager.kt (ByteBuddy — 152 lines)
  • Output jar reduced from ~4.9 MB to ~839 KB

0.8.0 — Async API, PxLuaNova embedded, documentation site

Section titled “0.8.0 — Async API, PxLuaNova embedded, documentation site”
  • Added mc.fetch(url) — coroutine-yielding HTTP requests via HttpClient.sendAsync
  • Added mc.sleep(ticks) — coroutine-yielding timed delay via Scheduler.schedule
  • Embedded PxLuaNova as a Gradle composite build (pxluanova/) — no more Maven Local dependency
  • Dependency auto-substitution via includeBuild 'pxluanova' in settings.gradle
  • New Astro + Starlight documentation site at site/ (brand: PxIgnis)
  • 28 documentation pages covering all APIs
  • .gitignore patterns fixed to match nested directories (trailing-slash form)
  • General cleanup and minor improvements

0.7.0 — Mob AI, ByteBuddy hooks, sidebar rewrite, PxLuaNova migration

Section titled “0.7.0 — Mob AI, ByteBuddy hooks, sidebar rewrite, PxLuaNova migration”
  • Using a custom fork of LuaJ with fixes & performance improvements.
  • Sidebar API enhancedplayer.sidebar property now returns a sidebar object with title/lines (r/w), show()/hide()/destroy()/setLine(n, text), and visible/lineCount properties. Assignment with {title, lines, visible} creates or updates the sidebar. Sidebar no longer auto-restores on reconnect — script must re-create it.
APIDescription
mc.registerBehaviour(id, fn)Registers a named AI behaviour function
mob:setAI("id") / mob:setAI(fn)Assigns behaviour by name or directly with a function. Persists on reload if ID is used
mob:clearAI()Removes behaviour, restores vanilla AI
mob.aiActiveWhether a behaviour is currently active

Mob properties (delegates to entity metatable for entity-level props):

PropertyTypeNotes
mob.isMobboolAlways true
mob.targetentity or nilAttack target (r/w by UUID)
mob.speednumberMovement speed attribute
mob.pathRemaining0–1Navigation progress fraction
mob.pathFoundboolWhether a path is active

Mob methods:

MethodSignature
mob:navigateTo(x, y, z) / mob:navigateTo(entity)Pathfind to position or entity
mob:stopNavigation()Cancel current path
mob:lookAt(x, y, z) / mob:lookAt(entity)Turn toward position or entity
mob:moveToward(vec, speed)Direct movement toward position
mob:jump()Trigger jump
mob:canSee(entity)Line-of-sight check
mob:distanceTo(entity) / mob:distanceTo(vec)Euclidean distance

world:spawn() and mc.getEntity() automatically return MobWrapper for MobEntity subtypes.

Built-in behaviours: guard, pet, orbiter, statue, wander (check demo_ai.lua).

ByteBuddy runtime method hooks (experimental, unstable)

Section titled “ByteBuddy runtime method hooks (experimental, unstable)”
FunctionDescription
mc.observeHook(class, method, callback)Hooks a Java method at runtime via ByteBuddy
mc.removeHook(class, method) → boolRemoves a hook
mc.clearHooks()Removes all hooks

Hooks are cleared on /ignis reload and server stop. Callback receives (instance, args). Only single-overload methods supported. May be removed in future versions.

FunctionDescription
mc.runtimeNamespaceCurrent mapping namespace (e.g. "named" or "intermediary")
mc.mapped(className)Map named class → runtime class name
mc.getMetatable("sidebar")Sidebar shared metatable
mc.getMetatable("mob")Mob shared metatable

mc.runtimeNamespace & mc.mapped(className) have no purpose btw, hooks map names automatically.

player.sidebar = { title = "Title", lines = {"Line 1", "Line 2"} }
local sb = player.sidebar
sb.title = "New Title" -- update title
sb.lines = {"Line 1", "Line 2"} -- replace all lines
sb:setLine(1, "Updated") -- update specific line
sb:show() / sb:hide() / sb:destroy()
sb.visible / sb.lineCount -- read-only properties

Coroutines are now supported!

FunctionDescription
mc.fetch(url)Simple HTTP GET, yields coroutine
mc.fetch({url, method?, headers?, body?, json?, timeout?})Full request table. json auto-encodes Lua table as JSON body. body and json are mutually exclusive
mc.sleep(ticks)Yields the coroutine for ticks ticks (20 = 1s), resumes on server thread

mc.fetch returns a response table with a shared metatable:

FieldDescription
res.oktrue for 2xx
res.statusHTTP status code
res.textRaw body string
res.headersResponse headers table
res.jsonParsed JSON. Throws LuaError on invalid JSON
res.errorSet only on network failures

0.6.0 — Vector arithmetic, Inventory/Container API, Raycast, shared metatables

Section titled “0.6.0 — Vector arithmetic, Inventory/Container API, Raycast, shared metatables”
  • Player.ktPlayerWrapper.kt, World.ktWorldWrapper.kt (internal refactor, no Lua API change)
  • player.world:particle(id, x, y, z)player.world:particle(id, Vec(x, y, z), opts?) (positional args replaced by vector + options table)
  • Removed dead coerce/KotlinToLua.kt
  • Vec(x, y, z) global constructor with +, -, *, /, unm, ==, tostring operators
  • Component-wise for v1 * v2, scalar for v / n, both v * n and n * v
  • Vector metatable accessible via mc.getMetatable("vec")
APIDescription
mc.createInventory(size)Creates a virtual SimpleInventory (9–54, multiple of 9)
inv:getItem(slot), inv:setItem(slot, item)Slot access (1-based)
inv:fill(item), inv:clear()Bulk ops
inv:open(player, title?)Opens chest screen → Container
container:onClick(callback)Registers click handler (auto-locks inventory)
container:onClick(nil)Unlocks inventory
container:close()Closes the screen

Both entity:raycast(range) and world:raycast(start, dir, range) now return a result table:

-- Block hit:
{ type = "block", blockPos = Vec(...), hit = Vec(...), side = "north", normal = Vec(...) }
-- Entity hit:
{ type = "entity", entity = EntityWrapper, hit = Vec(...) }
  • world:raycast(startVec, dirVec, range, includeFluids?, includeEntities?)
  • world:playSound(id, x, y, z, volume?, pitch?)
  • world:particle(id, pos, opts?) — vector position, options table with count, spread, speed, data
  • Item wrappers now use shared metatable (mc.getMetatable("item"))
  • mc.createItem full component table (name, lore, unbreakable, attackDamage, etc.)
  • ItemStackWrapper.toJson/fromJson for serialization
  • nbtToLua / luaToNbt utility functions in Utils.kt
  • chestgui.lua — chest GUI library with grid positioning
  • All wrappers now use shared metatables (one per type) — __index, __newindex, __pairs on the metatable, methods via rawset
  • player:damage(amount), player:heal(amount), player:give(id/count or ItemStack), player:setItem(slot, item), player:getItem(slot), player:clear()

No API changes. CI fix for Modrinth publish.


0.5.0 — Entity/Structure/Sidebar/Metatable APIs, 10 new events

Section titled “0.5.0 — Entity/Structure/Sidebar/Metatable APIs, 10 new events”
FunctionSignature
mc.getMetatable(name)"entity"|"player"|"world"|"structure" → shared metatable
mc.loadStructure(id)string → structure table
mc.loadStructureFile(path)file path → structure table
mc.getEntity(uuid)string → entity table or nil
mc.dump(value, maxDepth?)recursive table debug dump
mc.emit(event, ...)programmatic event firing
mc.playersproperty — list of online player tables (cached)
mc.onlineCountproperty — number of online players
MethodSignatureNotes
entity:readNbt()→ tableDump entity NBT to Lua table
entity:writeNbt(t)← tableWrite NBT back to entity
entity:raycast(range, includeFluids?)→ entity or {x,y,z} or nilHits entities before blocks
entity:damage(amount, sourceEntity?)With optional damage source
entity:addEffect(id, duration, amplifier?, particles?, icon?)→ boolStatus effect
entity:removeEffect(id)→ bool
entity:hasEffect(id)→ bool
entity:setOnFireFor(ticks)
entity.removedr/o boolWhether entity is removed
entity.pos.x/y/zr/w now livePreviously snapshot; now reads/writes current pos
MethodSignature
player:sendActionBar(text)
player:sendTitle(title, subtitle?, fadeIn?, stay?, fadeOut?)
player:playSound(id, volume?, pitch?)
player:getItem(slot) → item table or nil
player:setItem(slot, item)item from mc.createItem or nil
player:clear()Clear inventory
player.sidebarr/w property — set to {title="...", lines={...}} or nil to clear
player.sidebar.titler/w string
player.sidebar[i]r/w lines (1-indexed)
MethodSignatureNotes
world:particle(id, pos, opts)Moved from mc.particleParticle visible to all in that world
world:broadcastInRange(text, pos, range, overlay?)Moved from mc.broadcastInRange
world:getEntities(pos, radius, typeFilter?)→ table of entity tablesSpatial entity query
  • mc.particle(...) → use world:particle(...)
  • mc.broadcastInRange(...) → use world:broadcastInRange(...)

Cancellable:

  • player_block_break(player, pos, blockId)
  • player_block_place(player, pos, blockId)
  • player_use_item(player, hand, item, itemId)
  • player_attack_entity(player, entity)
  • player_interact_entity(player, entity)
  • player_hurt(player, source, amount)
  • entity_hurt(entity, source, amount)

Non-cancellable:

  • player_damage(player, source, damageTaken, blocked)
  • entity_damage(entity, source, damageTaken, blocked)
  • player_kill(player, killedEntity, source)
  • Structure table.size (vector), :place(world, pos, opts?) (rotation, mirror, on_entity)
  • Shared metatablesmc.getMetatable("entity"|"player"|"world"|"structure")
  • item.custom_model_data — now readable on item tables
  • mc.createItem(id, {attackDamage = N}) — new option to set attack damage
  • world:spawn(id, pos, overrides?) now returns entity with all new methods (raycast, damage, effects, NBT)
  • entity.dir, entity.bodyDir now return {x,y,z} vector tables (previously Java userdata)
  • Player cache: mc.players() and world.players reuse wrappers across lookups
  • Sidebar persists across world changes and reconnects (restored 2 ticks after join)
  • player.block_break/player.block_place migrated from mixins to Fabric API events

0.4.0 — World/Entity API, tags, item creation

Section titled “0.4.0 — World/Entity API, tags, item creation”
FunctionSignature
mc.world(name)string → world table
mc.createItem(id, countOrTable?)→ item table
mc.setBlock(x,y,z,block,world)Removed in 0.5.0 — use world:setBlock
mc.getBlock(x,y,z,world)Removed in 0.5.0 — use world:getBlock
mc.fill(x1,y1,x2,y2,z1,z2,block,world)Removed in 0.5.0 — use world:fill
MethodNotes
world:spawn(id, pos, overrides?)→ entity table
world:setBlock(pos, blockId)
world:getBlock(pos)→ block id string
world:fill(pos1, pos2, blockId)
world.name, world.time, world.raining, world.thunderingr/w properties

Entity table returned by world:spawn() and player delegation:

PropertyTypeNotes
uuidstring
typestringe.g. "minecraft:zombie"
name, displayName, customNamestring
worldtableWorld wrapper (was string in 0.3.0)
pos{x,y,z} vectorr/w (snapshot in 0.4.0; live proxy in 0.5.0)
dir, bodyDir{x,y,z} vector
health, maxHealthnumber
fallDistance, fireTicksnumber
glowing, invulnerableboolean
isSneaking, isSprintingboolean
air, maxAirnumber
tagsboolean proxypairs(entity.tags), entity.tags["tag"] = true/false
speed, armor, attackDamage, maxHealth_attr, followRange, knockbackResistance, luck, horseJump, flyingSpeed, armorToughness, movementEfficiency, scale, stepHeightnumberAttribute accessors (r/w where vanilla permits)
mainhand, offhand, head, chest, legs, feetitem table or nilEquipment slots (writable)
  • player.world now returns a world table (was a world name string)
  • player:give(id, count) also accepts item table from mc.createItem
  • All entity properties delegated to entity metatable (player inherits pos, health, tags, equipment, etc.)
  • word — single-word string (StringArgumentType.word())
  • Block IDs auto-prefixed with minecraft: if no namespace given
  • demo.lua no longer requires return {...} at file end
  • Shadow relocates org.luajru.pyxiion.lib.luaj and Permissions API

0.3.0 — Scheduler, teleport fix, Lua arg fallback

Section titled “0.3.0 — Scheduler, teleport fix, Lua arg fallback”
FunctionSignature
mc.schedule(delayTicks, callback)→ task id
mc.scheduleRepeating(delayTicks, intervalTicks, callback)→ task id
mc.cancelTask(id)→ bool
mc.data, player.dataPersistent key-value storage
  • server_start, server_stop
  • player_join(player) — cancellable
  • player_leave(player), player_death(player, source, message)
  • player_chat(player, message) — cancellable
  • player:send(msg), player:teleport(x,y,z), player:kick(reason?)
  • player:give(id, count), player:damage(amount)
  • player:getInventory() → table of item stacks
  • player:getBlockPos(){x,y,z}
  • player.name, player.uuid, player.world, player.pos (read-only snapshot)
  • player.health (r/w), player.displayName (r/w)
  • register(syntax, handler, permission?) — literal/arg syntax with <name:type> and [optional]
  • Types: text, player (alias target), int, double, float, bool, block_pos, choice=a,b,c
  • Reserved commands: stop, reload, op, deop, ban, ban-ip, pardon, pardon-ip, save-all, save-on, save-off, whitelist, pxrp
  • JSON storage: mc.data → global, player.data → per-player
  • Saved on server stop, player disconnect, /ignis reload
  • Nested tables require re-assignment (data.nested = t)
  • require "format"format(template), broadcastFormat(template)
  • require "simple"registerSimple(syntax, template, range?, overlay?)
  • Vec(x, y, z) constructor with arithmetic metamethods