Summary
map-server/src/lua/userdata.rs:2884:
methods.add_method("SetEventStatus", |_, _this, _status: Value| Ok(()));
The status argument is discarded and the method returns immediately —
nothing is pushed onto the script's CommandQueue, so a Lua-level call to
player:SetEventStatus(...) never produces a LuaCommand, never reaches
apply_*, and never emits a 0x0136 SetEventStatusPacket to the client.
This is a different code path from the system-level SetEventStatus
emission in world_manager.rs (build_set_event_status, used at spawn/init
to enable a director's or NPC's event conditions — that path is real and
already correctly wired, with detailed packet-capture-diffed comments
explaining exactly why it's needed). The bug is specifically in the
script-callable binding — when content Lua calls player:SetEventStatus(...)
directly (as opposed to the engine emitting it automatically at spawn), the
call is silently swallowed.
Where it's actually called
6 call sites across the man0l0 (Limsa)/man0u0 (Ul'dah) opening-tutorial
scripts, all toggling a push-trigger's enabled state in response to a quest
flag:
scripts/lua/unique/wil0Battle01/PopulaceStandard/ascilia.lua:17,27
scripts/lua/unique/wil0Battle01/PopulaceStandard/exit_trigger.lua:9
scripts/lua/unique/ocn0Battle02/PopulaceStandard/rostnsthal.lua
scripts/lua/unique/ocn0Battle02/PopulaceStandard/exit_door.lua
scripts/lua/unique/fst0Battle03/PopulaceStandard/yda.lua
scripts/lua/directors/OpeningDirector.lua:39,55
Example (exit_trigger.lua:9): once all three mini-tutorial flags are set,
the script calls player:SetEventStatus(npc, "pushDefault", true, 0x2) to
enable the exit door's push trigger so the player can walk out of the
tutorial area. With the binding a no-op, that enable never reaches the
client — the trigger's initial (disabled, or whatever world_manager.rs
set at spawn) state just... stays put. ascilia.lua uses the same call to
disable a trigger once its own step is done.
Given how much work has already gone into man0u0/man0l0 SEQ_000 softlock
fixes (e.g. PR #39 "fix(#25): Limsa Man0l0 SEQ_000 softlock"), this seemed
worth flagging directly — a script-level enable/disable that silently does
nothing is a plausible contributor to exactly this class of bug if any
remaining tutorial-progression softlocks trace back to one of these 6 call
sites.
Lineage note
The same conceptual bug exists in the original C# reference implementation:
Player.cs::SetEventStatus builds a SetEventStatusPacket but never calls
QueuePacket() on it (every sibling method — KickEventSpecial,
RunEventFunction, EndEvent — does). So this isn't something the Rust
port introduced; it looks like a faithful (if accidental) port of an
upstream gap. Filing here rather than (or in addition to) Bitbucket since
this port's implementation is directly reproducible and actively
maintained.
Suggested fix shape
Push a LuaCommand (something like SetEventStatus { player_id, actor_id, condition_name, enabled, kind }) from the binding, mirroring the existing
build_set_event_status packet builder in packets/send/actor.rs, and add
an apply_set_event_status in the runtime applier. I haven't attempted the
fix myself — didn't want to guess at the CommandQueue/applier plumbing
without being able to build and boot-test this repo locally — but wanted to
report it with the exact file/line and call sites since they were
straightforward to pin down.
Summary
map-server/src/lua/userdata.rs:2884:The
statusargument is discarded and the method returns immediately —nothing is pushed onto the script's
CommandQueue, so a Lua-level call toplayer:SetEventStatus(...)never produces aLuaCommand, never reachesapply_*, and never emits a0x0136 SetEventStatusPacketto the client.This is a different code path from the system-level SetEventStatus
emission in
world_manager.rs(build_set_event_status, used at spawn/initto enable a director's or NPC's event conditions — that path is real and
already correctly wired, with detailed packet-capture-diffed comments
explaining exactly why it's needed). The bug is specifically in the
script-callable binding — when content Lua calls
player:SetEventStatus(...)directly (as opposed to the engine emitting it automatically at spawn), the
call is silently swallowed.
Where it's actually called
6 call sites across the man0l0 (Limsa)/man0u0 (Ul'dah) opening-tutorial
scripts, all toggling a push-trigger's enabled state in response to a quest
flag:
Example (
exit_trigger.lua:9): once all three mini-tutorial flags are set,the script calls
player:SetEventStatus(npc, "pushDefault", true, 0x2)toenable the exit door's push trigger so the player can walk out of the
tutorial area. With the binding a no-op, that enable never reaches the
client — the trigger's initial (disabled, or whatever
world_manager.rsset at spawn) state just... stays put.
ascilia.luauses the same call todisable a trigger once its own step is done.
Given how much work has already gone into man0u0/man0l0 SEQ_000 softlock
fixes (e.g. PR #39 "fix(#25): Limsa Man0l0 SEQ_000 softlock"), this seemed
worth flagging directly — a script-level enable/disable that silently does
nothing is a plausible contributor to exactly this class of bug if any
remaining tutorial-progression softlocks trace back to one of these 6 call
sites.
Lineage note
The same conceptual bug exists in the original C# reference implementation:
Player.cs::SetEventStatusbuilds aSetEventStatusPacketbut never callsQueuePacket()on it (every sibling method —KickEventSpecial,RunEventFunction,EndEvent— does). So this isn't something the Rustport introduced; it looks like a faithful (if accidental) port of an
upstream gap. Filing here rather than (or in addition to) Bitbucket since
this port's implementation is directly reproducible and actively
maintained.
Suggested fix shape
Push a
LuaCommand(something likeSetEventStatus { player_id, actor_id, condition_name, enabled, kind }) from the binding, mirroring the existingbuild_set_event_statuspacket builder inpackets/send/actor.rs, and addan
apply_set_event_statusin the runtime applier. I haven't attempted thefix myself — didn't want to guess at the
CommandQueue/applier plumbingwithout being able to build and boot-test this repo locally — but wanted to
report it with the exact file/line and call sites since they were
straightforward to pin down.