antilua-lua-pipe
Use when controlling a running Antilua client via the named pipe IPC, sending Lua commands, interacting with the game world, crafting items, or manipulating player inventory. Also use when the client
SKILL.mdskills/antilua-lua-pipe/SKILL.md
GitHubAntilua Lua Pipe Control
Overview
The Antilua client exposes a named pipe (FIFO) at /tmp/antilua_lua (configurable via pipe_lua_path setting, requires pipe_lua_enable = true). Write JSON lines to send Lua code and read responses from an output file.
Protocol
Request is a JSON line (one per line, \n terminated):
{"code":"return 1+1", "file":"/tmp/resp"}
Response file format:
ok
2
On error, first line is error followed by the Lua error message.
Basic Commands
Always use unique response files to avoid races — and delete the response file before writing the request, otherwise a stale file from the previous request looks like an instant reply:
rm -f /tmp/r1
echo '{"code":"return 1+1", "file":"/tmp/r1"}' > /tmp/antilua_lua
sleep 0.3 && cat /tmp/r1
Better: use util/mcp/antilua_client.py (or the MCP server), which does
this per request, retries transient timeouts and paces writes for you.
Agent API (clientmods/ANTILUA/agent)
The agent clientmod exposes the building blocks for autonomous play over
the same pipe — prefer these over hand-rolling digs/places:
return agent.ctx(16) -- orientation snapshot
return agent.events(since_id, {"chat"}) -- event feed
return agent.go_to({x=1,y=2,z=3}, "walk") -- walk|sprint|fly|rtp|tp
return agent.dig({x,y,z}) -- verified dig (auto tool)
return agent.place({x,y,z}, "item:name") -- verified place
return agent.survival(true) -- survival preset
return agent.mem("note", "text") -- persistent memory
What NOT to do from a pipe chunk
- Don't
core.register_globalstep(...)— there is no unregister API, so the callback runs for the rest of the session (a strayset_autojumppulse will fly the player to the sky). Long-lived behaviour belongs in a clientmod with a guard flag. - Don't chain steps with
core.after(...)— timers registered from a one-shot pipe chunk don't reliably fire. Do one action per request and let the caller (or a clientmod) sequence them. - Don't busy-wait (
while os.clock() ...) — the pipe runs on the client's main thread; the client freezes and every later request times out. - Don't write invalid enum cheat settings (e.g.
killaura.target_mode = "true") — a globalstep error is fatal and shuts the client down. Stick to documented values /toggle_cheat.
Position & Movement
core.localplayer:get_pos() -- returns {x,y,z}
core.localplayer:set_pos({x=0, y=10, z=0}) -- teleport
core.localplayer:get_yaw() / get_pitch()
core.localplayer:set_yaw(90) / set_pitch(0) -- degrees
core.pos_to_string(pos, 0) -- "(X,Y,Z)"
Looking & Pointing
core.camera:get_look_dir() -- unit vector
core.camera:get_look_horizontal() -- radians
core.get_pointed_thing() -- raycast result
core.get_node_or_nil(pos) -- node at position
World Interaction
core.dig_node(pos) -- punch/dig a node
core.place_node(pos) -- place wielded item
core.find_nodes_near(pos, radius, nodenames, search_center)
Inventory
local inv = core.get_inventory("current_player")
inv.main -- 36-slot array of ItemStack objects
inv.craft -- 9-slot craft grid (server-dependent size)
inv.craftpreview -- 1-slot recipe preview (shapeless for 2x2, full for crafting table)
inv.craftresult -- 1-slot output
list[i]:get_name() -- item name
list[i]:get_count()-- stack count
Server Commands
core.run_server_chatcommand("cmd", "args") -- run /cmd args
core.get_privilege_list() -- {priv1=true, ...}
core.get_player_names() -- online player names
core.get_server_info() -- {address, ip, port, protocol_version}
Crafting System
Crafting requires InventoryAction userdata (client-side). Items move via inventory actions sent to the server.
Crafting Table
In MineClone2, the player has only a 2x2 craft grid (slots 1,2,4,5 in the 3x3 inv.craft array). A crafting table block is needed for 3x3 recipes.
A single dark oak log (mcl_trees:tree_dark_oak) placed in the 2x2 grid crafts into a crafting table (mcl_crafting_table:crafting_table). Place it on the ground within reach to unlock the 3x3 grid.
Moving Items
local a = InventoryAction("move")
a:from("current_player", "main", 3) -- source location, list, slot index
a:to("current_player", "craft", 1) -- destination
a:set_count(1) -- omit or 0 for entire stack
a:apply()
Triggering Craft
local a = InventoryAction("craft")
a:craft("current_player")
a:set_count(1) -- times to perform
a:apply()
Taking Results
local a = InventoryAction("move")
a:from("current_player", "craftresult", 1)
a:to("current_player", "main", target_slot)
a:set_count(0) -- 0 = move entire stack
a:apply()
Recipe Flow (Dark Oak → Wooden Pickaxe)
- Logs → Planks: Place 1 log in 2x2 grid → craft → take
mcl_trees:wood_dark_oak(8 planks) - Planks → Sticks: Place 2 planks vertically (slots 1 and 4) → craft →
mcl_core:stick(4) - Planks + Sticks → Pickaxe: Requires 3x3 grid (crafting table). 3 planks across top (slots 1,2,3) + 2 sticks down middle (slots 5,8) →
mcl_tools:pick_wood
Common Pitfalls
| Mistake | Fix |
|---|---|
| Stale response file read as the answer | rm -f the response file before writing the request; use antilua_client.py instead |
| Multiple commands share the same response file | Use unique file paths per request |
InventoryAction method chaining (:from():to()) doesn't return self |
Assign to local variable, call each method separately |
| Race: commands execute out of order | Add sleep 0.3 between pipe writes |
Table values returned as table: 0x... (unserializable) |
Use dump() or core.write_json(), or send "serialize": true in the request |
A string like "42" comes back as the number 42 |
The serializer coerces numeric-looking strings (Lua lua_isnumber("42") is true). Wrap the value in a table if the type matters |
set_lua_control seems to do nothing |
It is overwritten by Game::updatePlayerControl every frame — it only survives the frame the request ran in. Use set_autojump(true) for a jump pulse, or settings-based cheats |
globalstep registered from the pipe keeps running forever |
No unregister API — see "What NOT to do" above |
core.after chains from a pipe chunk never fire |
One action per request; sequence from the caller |
| Enum cheat setting with a bogus value | Fatal globalstep error kills the client — use documented values |
| Craft grid has items but craftpreview is empty | Might require server sync; check both craftresult and craftpreview |
core.get_wielded_item() vs core.localplayer:get_wielded_item() |
Use the latter (the former may not exist in some contexts) |
minetest.* alias vs core.* |
Use core.* (both work) |
Cheats
Toggle settings to enable built-in cheats:
core.settings:set_bool("jetpack", true) -- fly with jump key
core.settings:set_bool("fullbright", true) -- max light
core.settings:set_bool("fastdig", true) -- fast mining
core.settings:set_bool("autojump", true) -- auto-jump obstacles
See doc/al_csm_api.md for the full list of cheat settings.
MCP Server Alternative
If an MCP host has the antilua-mcp server registered, prefer its structured
tools over raw pipe writes (see skills/antilua-mcp/SKILL.md). It wraps this
same pipe, returns JSON, and covers player/world/inventory/server/cheat actions
plus a scene-only screenshot() — no response-file management or sleep
pacing needed. Raw pipe access remains available for anything the tools don't
cover (via run_lua).