Lua scripts provide a sandboxed scripting tier for OpenBC mods. They run in a Lua 5.4 environment with access to a subset of the engine API through the obc global table.
Register a Lua script as a module in server.toml:
[[modules]]
name = "my_rules"
lua = "mods/my-mod/rules.lua"
[modules.config]
bonus_points = 500
overtime_enabled = trueThe engine loads the script, executes it in a sandboxed Lua state, and registers any event handlers it defines.
All engine interaction goes through the obc global table:
-- Subscribe to an event
obc.on("ship_killed", function(ctx)
obc.log("Kill: slot " .. ctx.killer_slot .. " -> " .. ctx.victim_slot)
end, 150) -- priority 150
-- Unsubscribe (save the function reference)
local my_handler = function(ctx) ... end
obc.on("ship_killed", my_handler, 100)
obc.off("ship_killed", my_handler)
-- Fire a custom event
obc.fire("my_mod_event", { bonus = 500 })obc.peer_count() -- Number of connected players
obc.peer_max() -- Max player capacity
obc.peer_active(slot) -- Is slot occupied? (boolean)
obc.peer_name(slot) -- Player name (string)
obc.peer_team(slot) -- Team index (0 = FFA)obc.ship_hull(slot) -- Current hull HP
obc.ship_hull_max(slot) -- Max hull HP
obc.ship_alive(slot) -- Is ship alive? (boolean)
obc.ship_species(slot) -- Species ID
obc.subsystem_hp(slot, idx) -- Subsystem current HP
obc.subsystem_hp_max(slot, idx) -- Subsystem max HP
obc.subsystem_count(slot) -- Number of subsystemsobc.ship_apply_damage(slot, amount, source_slot)
obc.ship_apply_subsystem_damage(slot, subsys_idx, amount)
obc.ship_kill(slot, killer_slot, "weapon") -- method: "weapon", "collision", "self_destruct", "explosion"
obc.ship_respawn(slot)obc.score_add(slot, kills, deaths, points)
obc.score_kills(slot)
obc.score_deaths(slot)
obc.score_points(slot)
obc.score_reset_all()obc.config_string("key", "default")
obc.config_int("key", 0)
obc.config_float("key", 1.0)
obc.config_bool("key", false)Reads from the module's [modules.config] section in TOML.
local id = obc.timer_add(5.0, function()
obc.log("5 seconds elapsed")
end)
obc.timer_remove(id)obc.send_reliable(slot, data) -- Send bytes to one player
obc.send_to_all(data, true) -- Send to all (reliable)
obc.send_to_others(slot, data, true) -- Send to all except oneobc.log("Info message")
obc.log_warn("Warning message")
obc.log_debug("Debug message")
obc.log_error("Error message")obc.game_time() -- Elapsed seconds (float)
obc.game_map() -- Current map name
obc.game_in_progress() -- Is round active? (boolean)Event handlers receive a ctx table with event-specific fields:
obc.on("ship_killed", function(ctx)
-- ctx.event_name = "ship_killed"
-- ctx.sender_slot = player who triggered the event
-- ctx.killer_slot = who made the kill
-- ctx.victim_slot = who was killed
-- ctx.method = "weapon", "collision", etc.
-- Cancel further handlers:
ctx.cancelled = true
-- Suppress network relay:
ctx.suppress_relay = true
end, 50)Each event type has its own context fields. See Event System for the complete catalog.
-- mods/instagib/rules.lua
-- One-hit kills: any damage instantly kills the target
obc.on("collision_effect", function(ctx)
-- Kill on any collision
if obc.ship_alive(ctx.victim_slot) then
obc.ship_kill(ctx.victim_slot, ctx.attacker_slot, "collision")
end
ctx.cancelled = true -- Skip stock damage calculation
end, 25) -- Priority 25: before stock combat (50)
obc.on("beam_fire", function(ctx)
if obc.ship_alive(ctx.target_slot) then
obc.ship_kill(ctx.target_slot, ctx.sender_slot, "weapon")
end
ctx.cancelled = true
end, 25)
obc.on("ship_killed", function(ctx)
-- Double points in instagib
obc.score_add(ctx.killer_slot, 0, 0, 200)
end, 150) -- After stock scoring
obc.log("Instagib mode loaded!")Lua scripts run in a sandbox. The following standard libraries are not available:
| Removed | Reason |
|---|---|
io |
No filesystem access |
os |
No system calls |
debug |
No runtime introspection |
loadfile / dofile |
No arbitrary file loading |
package / require |
No external module loading |
Available standard libraries: string, table, math, coroutine (limited), utf8.
| Limit | Default | Purpose |
|---|---|---|
| Memory | 16 MB | Prevents runaway allocation |
| Instructions | 1,000,000 per handler call | Prevents infinite loops |
If a script exceeds either limit, the handler call is terminated and an error is logged. The script remains loaded -- only the offending handler invocation is killed.
A mod can register multiple Lua scripts as separate modules:
[[modules]]
name = "instagib_rules"
lua = "mods/instagib/rules.lua"
[[modules]]
name = "instagib_announcer"
lua = "mods/instagib/announcer.lua"Each script runs in its own Lua state. They communicate through events, not shared variables.
- Getting Started -- Modding overview
- TOML Reference -- Configuration formats
- Event System -- Full event catalog
- DLL Module Guide -- C module development (full API access)