diff --git a/dev/tests/example.lua b/dev/tests/example.lua index 89302aa4..1ebe6b06 100644 --- a/dev/tests/example.lua +++ b/dev/tests/example.lua @@ -1,6 +1,8 @@ test.set_setting("chunks.load-distance", 3) test.set_setting("chunks.load-speed", 1) +test.quit() + test.reconfig_packs({"base"}, {}) test.new_world("demo", "2019", "core:default") local pid1 = player.create("Xerxes") diff --git a/res/scripts/stdlib.lua b/res/scripts/stdlib.lua index 0298a2b9..9ddda13c 100644 --- a/res/scripts/stdlib.lua +++ b/res/scripts/stdlib.lua @@ -9,16 +9,41 @@ function sleep(timesec) end end +function tb_frame_tostring(frame) + local s = frame.short_src + if frame.what ~= "C" then + s = s .. ":" .. tostring(frame.currentline) + end + if frame.what == "main" then + s = s .. ": in main chunk" + elseif frame.name then + s = s .. ": in function " .. utf8.escape(frame.name) + end + return s +end + if test then test.sleep = sleep test.name = __VC_TEST_NAME test.new_world = core.new_world test.open_world = core.open_world test.close_world = core.close_world + test.reopen_world = core.reopen_world test.reconfig_packs = core.reconfig_packs test.set_setting = core.set_setting test.tick = coroutine.yield + function test.quit() + local tb = debug.get_traceback(1) + local s = "test.quit() traceback:" + for i, frame in ipairs(tb) do + s = s .. "\n\t"..tb_frame_tostring(frame) + end + debug.log(s) + core.quit() + coroutine.yield() + end + function test.sleep_until(predicate, max_ticks) max_ticks = max_ticks or 1e9 local ticks = 0 @@ -382,7 +407,9 @@ end function __vc_stop_coroutine(id) local co = __vc_coroutines[id] if co then - coroutine.close(co) + if coroutine.close then + coroutine.close(co) + end __vc_coroutines[id] = nil end end diff --git a/src/ServerMainloop.cpp b/src/ServerMainloop.cpp index be013e7b..dd50f089 100644 --- a/src/ServerMainloop.cpp +++ b/src/ServerMainloop.cpp @@ -41,6 +41,11 @@ void ServerMainloop::run() { double delta = targetDelta; auto begin = steady_clock::now(); while (process->isActive()) { + if (engine.isQuitSignal()) { + process->terminate(); + logger.info() << "script has been terminated due to quit signal"; + break; + } time.step(delta); process->update(); if (controller) { diff --git a/src/engine.cpp b/src/engine.cpp index 46fd17c8..095d744c 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -461,6 +461,17 @@ void Engine::onWorldClosed() { levelConsumer(nullptr); } +void Engine::quit() { + quitSignal = true; + if (!isHeadless()) { + Window::setShouldClose(true); + } +} + +bool Engine::isQuitSignal() const { + return quitSignal; +} + gui::GUI* Engine::getGUI() { return gui.get(); } diff --git a/src/engine.hpp b/src/engine.hpp index 88a60865..39792624 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -74,6 +74,7 @@ class Engine : public util::ObjectsKeeper { std::unique_ptr gui; Time time; consumer> levelConsumer; + bool quitSignal = false; void loadControls(); void loadSettings(); @@ -138,6 +139,10 @@ public: void onWorldOpen(std::unique_ptr level); void onWorldClosed(); + void quit(); + + bool isQuitSignal() const; + /// @brief Get current Content instance const Content* getContent() const; diff --git a/src/logic/scripting/lua/libs/libcore.cpp b/src/logic/scripting/lua/libs/libcore.cpp index d2d0c80f..7b121be8 100644 --- a/src/logic/scripting/lua/libs/libcore.cpp +++ b/src/logic/scripting/lua/libs/libcore.cpp @@ -56,6 +56,9 @@ static int l_open_world(lua::State* L) { /// @brief Reopen world static int l_reopen_world(lua::State*) { auto controller = engine->getController(); + if (level == nullptr) { + throw std::runtime_error("no world open"); + } controller->reopenWorld(level->getWorld()); return 0; } @@ -229,7 +232,7 @@ static int l_open_folder(lua::State* L) { /// @brief Quit the game static int l_quit(lua::State*) { - Window::setShouldClose(true); + engine->quit(); return 0; }