events.on(fn) ignores the handler's annotation at runtime.
The AST pass collects every events.on(function(e: EventX)) annotation per file in source order into state.inferred_events and each runtime events.on call consumes the next entry (script.cpp:518, mods.cpp:149). That only holds while call order matches parse order.
A file is parsed in full before it executes, a module that subscribes while being require'd consumes entries belonging to the file that required it and every later binding shifts by one.
Repro:
-- mod/runtime.luau
local combat = require("mymod/combat") -- parsed after this file
events.on(function(e: EventPlayerJoin) ... end)
-- mod/combat.luau
events.on(function(e: EventHit) ... end)
events.on(function(e: EventTick) ... end)
Parsed order: player_join, hit, tick
Call order: combat's two first
Result: combat's hit handler runs on player_join, its tick handler runs on hit and the join handler runs on every tick.
In my case that surfaced as "attempt to index nil with 'control'" sixty times a second because the join handler was getting a tick event.
events.on(fn)ignores the handler's annotation at runtime.The AST pass collects every
events.on(function(e: EventX))annotation per file in source order intostate.inferred_eventsand each runtimeevents.oncall consumes the next entry (script.cpp:518, mods.cpp:149). That only holds while call order matches parse order.A file is parsed in full before it executes, a module that subscribes while being require'd consumes entries belonging to the file that required it and every later binding shifts by one.
Repro:
Parsed order: player_join, hit, tick
Call order: combat's two first
Result: combat's hit handler runs on player_join, its tick handler runs on hit and the join handler runs on every tick.
In my case that surfaced as "attempt to index nil with 'control'" sixty times a second because the join handler was getting a tick event.