geode.ScriptEvent is the built-in event bridge between Luau scripts and C++.
Both directions use string topics and optional string payloads.
Lua geode.ScriptEvent.post reaches every C++ listener only.
C++ imes::luauapi::postScriptEvent reaches C++ and Lua listeners.
listen matches every topic, listenFor matches one topic.
geode.ScriptEvent.post(topic: string, payload: string?) -> ()Posts to C++ LuaScriptEvent listeners. The payload defaults to "".
local modId = geode.Mod.getID()
geode.ScriptEvent.post(modId .. "/player.died", "67")geode.ScriptEvent.listen(callback: (topic: string, payload: string) -> boolean?, priority: number?) -> ScriptEventListenerHandleMatches every topic. Callbacks only observe C++ posts, because Lua posts never echo back into Lua listeners.
local handle = geode.ScriptEvent.listen(function(topic, payload)
print("script event", topic, payload)
return false
end)geode.ScriptEvent.listenFor(topic: string, callback: (topic: string, payload: string) -> boolean?, priority: number?) -> ScriptEventListenerHandleMatches one exact topic. Fires only when a C++ mod posts that topic.
local modId = geode.Mod.getID()
geode.ScriptEvent.listenFor(modId .. "/game.paused", function(_, payload)
print("paused by", payload)
end)Convert numbers with tostring and tonumber.
local modId = geode.Mod.getID()
geode.ScriptEvent.post(modId .. "/player.jumped", tostring(12))
geode.ScriptEvent.listenFor(modId .. "/player.jumped", function(_, payload)
local level = tonumber(payload)
if not level then return end
print("jumped in level", level)
end)tonumber returns nil for non-numeric payloads, so guard before use.
handle:disconnect() -> ()Disconnects a listener handle. Store the handle while the listener is active. Handles also disconnect during garbage collection and runtime shutdown.
Prefix every topic with your mod id and a slash, like node ids.
This is the event version of Geode's _spr rule.
See LuauAPI mod guidelines.
geode.Mod.getID() returns your mod id, for example my.mod-id.
local modId = geode.Mod.getID()
geode.ScriptEvent.post(modId .. "/player.jumped", "level-1")
geode.ScriptEvent.listenFor(modId .. "/game.paused", function(_, payload)
print("paused by", payload)
end)C++ posters use the same prefix with geode::Mod::get()->getID().
Return true from the callback to stop propagation to later listeners.
Return false or nothing to let the event continue.
Stopping also prevents listenFor callbacks for the same topic from running.
For C++ posts (postScriptEvent), a C++ LuaScriptEvent listener returning true stops Lua listeners too.
If a callback errors or times out, LuauAPI logs it and lets propagation continue.
A C++ mod posts from include/ScriptEvents.hpp:
auto modId = geode::Mod::get()->getID();
imes::luauapi::postScriptEvent(modId + "/game.paused", "auto");The optional priority argument works like other Geode event listeners.
Smaller priority values run first (e.g. -1000 before 0).
Topics and payloads are strings only, with no history or wildcard matching.
Posts during shutdown are dropped.
C++ LuaScriptEvent listeners still receive posts before the runtime is ready.
Caps, deadlines, and error strings live in Limits and errors.
- Keyboard input
- Mouse input
- callbacks
- globals
- Type stubs
- C++ API reference
- Getting started
- Sharing APIs between mods
include/ScriptEvents.hppsrc/bindings/geode/GeodeScriptEventBinding.cppsrc/bindings/geode/ScriptEventInternal.hppsrc/api.cpptools/luau_codegen/extra_bindings/scriptevent.dluau