"""Compile the production reload/bindings with real Lua and deterministic server doubles.""" import pathlib import subprocess import tempfile ROOT = pathlib.Path(__file__).resolve().parents[2] SOURCE = (ROOT / "src/server/game/World/ClientlessLuaEngine.cpp").read_text() def function(signature): start = SOURCE.index(signature) opening = SOURCE.index("{", start) depth = 1 end = opening + 1 while depth: depth += (SOURCE[end] == "{") - (SOURCE[end] == "}") end += 1 return SOURCE[start:end] HARNESS = r''' #include #include #include #include #include #include #include using uint32 = uint32_t; #define TC_LOG_INFO(...) ((void)0) struct ClientlessIntent { std::string Name; std::unordered_map Parameters; bool Empty() const { return Name.empty(); } }; using ClientlessAction = ClientlessIntent; struct ClientlessAdventureProfile { int Revision = 0; }; struct Guid { uint32 GetCounter() const { return 26; } }; struct Player { bool aboard = false, flying = false, teleport = false; Guid GetGUID() { return {}; } bool IsInWorld() { return true; } bool GetTransport() { return aboard; } bool IsInFlight() { return flying; } bool IsBeingTeleported() { return teleport; } }; enum class ClientlessActionExecutionState { Idle, SelectingTarget, Moving, Completed, Failed }; enum class ClientlessTravelState { Idle, Moving, Arrived }; struct ActionStatus { ClientlessActionExecutionState State = ClientlessActionExecutionState::Idle; }; struct TravelStatus { ClientlessTravelState State = ClientlessTravelState::Idle; }; struct Manager { bool managed = true; std::vector actions; ClientlessIntent intent; bool IsManaged(uint32) { return managed; } unsigned GetQueuedActionCount(uint32) { return actions.size(); } bool QueueAction(uint32, ClientlessAction a) { actions.push_back(std::move(a)); return true; } bool SetIntent(uint32, ClientlessIntent i) { intent = std::move(i); return true; } bool ClearIntent(uint32) { intent = {}; return true; } } manager; struct Executor { ActionStatus status; ActionStatus* GetStatus(uint32) { return &status; } } executor; struct Travel { TravelStatus status; TravelStatus* GetStatus(uint32) { return &status; } } travel; auto* sClientlessPlayerMgr = &manager; auto* sClientlessActionExecutorMgr = &executor; auto* sClientlessTravelMgr = &travel; class ClientlessLuaEngine { public: std::unordered_map _profiles; std::string _scriptRoot = "/test"; std::string script; bool profileOK = true; bool LoadAdventureProfile(Player*) { if (!profileOK) return false; _profiles[26].Revision++; return true; } bool ExecuteAdventureScript(Player*); bool ReloadAdventureScript(Player*, std::string&); }; ''' HARNESS += SOURCE[SOURCE.index("struct ReloadEffects"):SOURCE.index("uint32 BoundGuid")] for signature in ["uint32 BoundGuid", "std::unordered_map ReadMap", "int LuaIntend", "int LuaClearIntent", "int LuaQueueAction"]: HARNESS += function(signature) + "\n" HARNESS += r''' bool ClientlessLuaEngine::ExecuteAdventureScript(Player*) { lua_State* L = luaL_newstate(); luaL_openlibs(L); for (auto entry : {std::pair{"intend", LuaIntend}, {"clear_intent", LuaClearIntent}, {"queue_action", LuaQueueAction}}) { lua_pushinteger(L,26); lua_pushcclosure(L,entry.second,1); lua_setglobal(L,entry.first); } bool success = luaL_loadstring(L, script.c_str()) == LUA_OK && lua_pcall(L,0,0,0) == LUA_OK; lua_close(L); return success; } ''' HARNESS += function("bool ClientlessLuaEngine::ReloadAdventureScript") HARNESS += r''' int main() { Player p; ClientlessLuaEngine engine; std::string detail; engine._profiles[26].Revision = 4; manager.intent.Name = "old"; engine.script = "queue_action('move', {node='tram_if'}); intend('new'); error('failure')"; assert(!engine.ReloadAdventureScript(&p,detail)); assert(manager.actions.empty() && manager.intent.Name == "old"); assert(engine._profiles.at(26).Revision == 4 && !pendingReload); engine.script = "this is invalid lua"; assert(!engine.ReloadAdventureScript(&p,detail)); assert(engine._profiles.at(26).Revision == 4 && manager.actions.empty()); engine.profileOK = false; assert(!engine.ReloadAdventureScript(&p,detail)); engine.profileOK = true; engine.script = "assert(not queue_action('')); queue_action('move', {node='tram_if'}); clear_intent(); intend('new')"; assert(engine.ReloadAdventureScript(&p,detail)); assert(manager.actions.size() == 1 && manager.actions[0].Parameters.at("node") == "tram_if"); assert(manager.intent.Name == "new" && engine._profiles.at(26).Revision == 5); assert(!engine.ReloadAdventureScript(&p,detail)); // queued actions must not duplicate manager.actions.clear(); executor.status.State = ClientlessActionExecutionState::Moving; assert(!engine.ReloadAdventureScript(&p,detail)); executor.status.State = ClientlessActionExecutionState::Completed; travel.status.State = ClientlessTravelState::Moving; assert(!engine.ReloadAdventureScript(&p,detail)); travel.status.State = ClientlessTravelState::Arrived; p.aboard = true; assert(!engine.ReloadAdventureScript(&p,detail)); p.aboard = false; p.flying = true; assert(!engine.ReloadAdventureScript(&p,detail)); p.flying = false; p.teleport = true; assert(!engine.ReloadAdventureScript(&p,detail)); p.teleport = false; assert(!engine.ReloadAdventureScript(nullptr,detail)); manager.managed = false; assert(!engine.ReloadAdventureScript(&p,detail)); manager.managed = true; engine._profiles.clear(); engine.script = "error('failure')"; assert(!engine.ReloadAdventureScript(&p,detail) && engine._profiles.empty()); engine.script = "clear_intent()"; assert(engine.ReloadAdventureScript(&p,detail) && manager.intent.Empty()); } ''' with tempfile.TemporaryDirectory(prefix="vcore-lua-reload-") as folder: source = pathlib.Path(folder) / "test.cpp" binary = pathlib.Path(folder) / "test" source.write_text(HARNESS) flags = subprocess.check_output(["pkg-config", "--cflags", "--libs", "lua5.4"], text=True).split() subprocess.run(["c++", "-std=c++17", str(source), "-o", str(binary), *flags], check=True) subprocess.run([str(binary)], check=True) print("Lua reload regression tests passed (production reload/bindings, real Lua 5.4).")