From 742209988bef7e7bc3c0a09c8e1529309e21d325 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Fri, 26 Nov 2021 15:45:36 -0500 Subject: [PATCH] added /tick command --- luprex/core/cpp/invocation.cpp | 1 + luprex/core/cpp/invocation.hpp | 1 + luprex/core/cpp/lpxclient.cpp | 7 ++++++- luprex/core/cpp/lpxserver.cpp | 5 +++++ luprex/core/cpp/luaconsole.cpp | 6 ++---- luprex/core/cpp/textgame.cpp | 2 +- luprex/core/cpp/world-core.cpp | 30 ++++++++++++++++++++++-------- luprex/core/cpp/world.hpp | 20 ++++++++++++++------ luprex/core/lua/login.lua | 10 +++++++++- 9 files changed, 61 insertions(+), 21 deletions(-) diff --git a/luprex/core/cpp/invocation.cpp b/luprex/core/cpp/invocation.cpp index 53b10fdd..9e483b9a 100644 --- a/luprex/core/cpp/invocation.cpp +++ b/luprex/core/cpp/invocation.cpp @@ -63,6 +63,7 @@ std::string Invocation::debug_string() { case KIND_PLAN: oss << "plan"; break; case KIND_LUA: oss << "lua"; break; case KIND_FLUSH_PRINTS: oss << "flush_prints"; break; + case KIND_TICK: oss << "tick"; break; default: oss << "UNKNOWN"; break; } oss << " a=" << actor_; diff --git a/luprex/core/cpp/invocation.hpp b/luprex/core/cpp/invocation.hpp index 0f970694..1c7beaea 100644 --- a/luprex/core/cpp/invocation.hpp +++ b/luprex/core/cpp/invocation.hpp @@ -22,6 +22,7 @@ public: KIND_PLAN, KIND_LUA, KIND_FLUSH_PRINTS, + KIND_TICK, }; private: diff --git a/luprex/core/cpp/lpxclient.cpp b/luprex/core/cpp/lpxclient.cpp index bf25589b..36563df1 100644 --- a/luprex/core/cpp/lpxclient.cpp +++ b/luprex/core/cpp/lpxclient.cpp @@ -130,6 +130,10 @@ public: send_invocation(inv); } + void do_tick_command(const util::StringVec &words) { + send_invocation(Invocation(Invocation::KIND_TICK, actor_id_, actor_id_, "")); + } + void do_quit_command(const util::StringVec &words) { abandon_server(); stop_driver(); @@ -142,8 +146,9 @@ public: else if (words[0] == "syntax") do_syntax_command(words); else if (words[0] == "view") do_view_command(words); else if (words[0] == "menu") do_menu_command(words); - else if (words[0] == "quit") do_quit_command(words); else if (words[0] == "choose") do_choose_command(words); + else if (words[0] == "tick") do_tick_command(words); + else if (words[0] == "quit") do_quit_command(words); else { stdostream() << "Unsupported command: " << words[0] << std::endl; } diff --git a/luprex/core/cpp/lpxserver.cpp b/luprex/core/cpp/lpxserver.cpp index 709a76d0..5306163a 100644 --- a/luprex/core/cpp/lpxserver.cpp +++ b/luprex/core/cpp/lpxserver.cpp @@ -59,6 +59,10 @@ public: stdostream() << "Syntax Error: " << words[1] << std::endl; } + void do_tick_command(const util::StringVec &words) { + master_->invoke(Invocation(Invocation::KIND_TICK, admin_id_, admin_id_, "")); + } + void do_quit_command(const util::StringVec &words) { stop_driver(); } @@ -68,6 +72,7 @@ public: else if (words[0] == "luainvoke") do_luainvoke_command(words); else if (words[0] == "luaprobe") do_luaprobe_command(words); else if (words[0] == "syntax") do_syntax_command(words); + else if (words[0] == "tick") do_tick_command(words); else if (words[0] == "quit") do_quit_command(words); else { stdostream() << "Unsupported command: " << words[0] << std::endl; diff --git a/luprex/core/cpp/luaconsole.cpp b/luprex/core/cpp/luaconsole.cpp index a1c90b34..96274044 100644 --- a/luprex/core/cpp/luaconsole.cpp +++ b/luprex/core/cpp/luaconsole.cpp @@ -79,10 +79,8 @@ void LuaConsole::simplify(const StringVec &words) { synerr("/quit takes no arguments"); } } else if (words[0] == "tick") { - if ((words.size() == 2)&&(util::validinteger(words[1]))) { - // OK - } else { - synerr("/tick [optional-timestamp]"); + if (words.size() != 1) { + synerr("/tick takes no arguments"); } } else { synerr("unrecognized command"); diff --git a/luprex/core/cpp/textgame.cpp b/luprex/core/cpp/textgame.cpp index 618cdb6f..2749b370 100644 --- a/luprex/core/cpp/textgame.cpp +++ b/luprex/core/cpp/textgame.cpp @@ -64,7 +64,7 @@ private: } void do_tick_command(const StringVec &cmd) { - world_->run_scheduled_threads(util::strtoint(cmd[1], -1)); + world_->invoke(Invocation(Invocation::KIND_TICK, actor_id_, actor_id_, "")); } void do_quit_command(const StringVec &cmd) { diff --git a/luprex/core/cpp/world-core.cpp b/luprex/core/cpp/world-core.cpp index 712a0583..9cfbd42d 100644 --- a/luprex/core/cpp/world-core.cpp +++ b/luprex/core/cpp/world-core.cpp @@ -69,6 +69,9 @@ World::World(util::WorldType wt) { source_db_.init(state()); std::string srcerrs = source_db_.rebuild(); + // Clear the clock. + clock_ = 0; + // There shouldn't be any lua errors in the sourceDB at this // point, since there's no lua code in the sourceDB. assert(srcerrs == ""); @@ -402,6 +405,8 @@ void World::invoke(const Invocation &inv) { case Invocation::KIND_FLUSH_PRINTS: invoke_flush_prints(inv.actor(), inv.place(), inv.action(), inv.data()); break; + case Invocation::KIND_TICK: + invoke_tick(inv.actor(), inv.place(), inv.action(), inv.data()); default: // Do nothing. Standard behavior for any invalid command is to // simply do nothing at all. Perhaps eventually we may add a flag @@ -576,18 +581,25 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a // Push the thread's ID into the runnable thread queue, // then run the thread queue. thread_sched_.add(0, tid, place_id); - run_scheduled_threads(0); + run_scheduled_threads(); assert(stack_is_clear()); } -void World::run_scheduled_threads(int64_t clk) { +void World::invoke_tick(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data) { + if (util::world_type_authoritative(world_type_)) { + clock_ += 1; + run_scheduled_threads(); + } +} + +void World::run_scheduled_threads() { assert(stack_is_clear()); lua_State *L = state(); LuaVar tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread; LuaStack LS(L, tangibles, place, mt, threads, thinfo, actorid, isnew, useppool, thread); LS.rawget(tangibles, LuaRegistry, "tangibles"); - while (thread_sched_.ready(clk)) { + while (thread_sched_.ready(clock_)) { SchedEntry sched = thread_sched_.pop(); LS.rawget(place, tangibles, sched.place_id()); if (!LS.istable(place)) { @@ -632,7 +644,7 @@ void World::run_scheduled_threads(int64_t clk) { if (status == LUA_YIELD) { // If there's nothing on the stack, infer that tangible.nopredict yielded. if (lua_gettop(CO) == 0) { - std::cerr << "Thread killed self using tangible.nopredict" << std::endl; + // std::cerr << "Thread killed self using tangible.nopredict" << std::endl; LS.rawset(threads, sched.thread_id(), LuaNil); } // If there's a single number on the stack, infer that 'wait' yielded. @@ -641,9 +653,9 @@ void World::run_scheduled_threads(int64_t clk) { lua_settop(CO, 0); LS.rawset(thinfo, "isnew", false); LS.rawset(thinfo, "useppool", false); - std::cerr << "Thread wait = " << delay << std::endl; + // std::cerr << "Thread wait = " << delay << std::endl; thread_sched_.add(sched.clock() + int64_t(delay), sched.thread_id(), sched.place_id()); - std::cerr << "Added to schedule." << std::endl; + // std::cerr << "Added to schedule." << std::endl; } // In any other case, generate an error and kill the coroutine. else { @@ -652,12 +664,12 @@ void World::run_scheduled_threads(int64_t clk) { } } else if (status == LUA_OK) { // Successfully ran to completion. Remove from thread table. - std::cerr << "Thread ran to completion." << std::endl; + // std::cerr << "Thread ran to completion." << std::endl; LS.rawset(threads, sched.thread_id(), LuaNil); } else { // Generated an error. Add a traceback, print, and kill the coroutine. traceback_coroutine(CO); - std::cerr << lua_tostring(CO, -1); + // std::cerr << lua_tostring(CO, -1); LS.rawset(threads, sched.thread_id(), LuaNil); } } @@ -733,6 +745,7 @@ void World::serialize(StreamBuffer *sb) { // int64_t wc0 = sb->total_writes(); lua_snap_.serialize(sb); id_global_pool_.serialize(sb); + sb->write_int64(clock_); thread_sched_.serialize(sb); sb->write_uint32(tangibles_.size()); for (const auto &p : tangibles_) { @@ -749,6 +762,7 @@ void World::deserialize(StreamBuffer *sb) { redirects_.clear(); lua_snap_.deserialize(sb); id_global_pool_.deserialize(sb); + clock_ = sb->read_int64(); thread_sched_.deserialize(sb); // Mark all tangibles for deletion by setting ID to zero. for (const auto &p : tangibles_) { diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index 765cfbb8..4839f847 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -66,8 +66,9 @@ public: // Print Buffer // - // In non-actors, this is a null pointer. Stores the console - // output for this actor until it can be probed by the client. + // Stores the console output for this actor until it can be + // probed by the client. Most tangibles have empty printbuffers, + // which are stored as just a null pointer internally. // PrintBuffer print_buffer_; @@ -124,9 +125,9 @@ public: // Make a tangible. // - // If the ID is zero, allocates an ID using the thread's ID allocator. If - // pushdb is true, pushes the tangible's database onto the lua stack. - // Otherwise, leaves the lua stack untouched. + // You must provide a valid previously-unused ID. If pushdb is true, pushes + // the tangible's database onto the lua stack. Otherwise, leaves the lua + // stack untouched. // Tangible *tangible_make(lua_State *L, int64_t id, const std::string &plane, bool pushdb); @@ -220,7 +221,7 @@ public: // Run any threads which according to the scheduler queue are ready. // - void run_scheduled_threads(int64_t clk); + void run_scheduled_threads(); // Check that the main thread has nothing on the stack // @@ -271,6 +272,10 @@ private: // Invoke the flush-prints operation. // void invoke_flush_prints(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data); + + // Invoke the tick operation. + // + void invoke_tick(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data); public: //////////////////////////////////////////////////////////////////////////// @@ -453,6 +458,9 @@ private: // std::unordered_map tangibles_; + // Current time. + int64_t clock_; + // Thread schedule: must include every thread, except // for the one currently-executing thread. // diff --git a/luprex/core/lua/login.lua b/luprex/core/lua/login.lua index 5c8df9c7..44daff6f 100644 --- a/luprex/core/lua/login.lua +++ b/luprex/core/lua/login.lua @@ -2,12 +2,21 @@ maketangible('login') function login.interface(actor, place) gui.menu_item("becomeplayer", "Become a Player") + gui.menu_item("p123", "Print 1, 2, 3") end function login.action.becomeplayer(actor, place, dialog) tangible.setclass(actor, player) end +function login.action.p123(actor, place, dialog) + print(1) + wait(1) + print(2) + wait(1) + print(3) +end + function setfoo(n) tangible.nopredict() tangible.actor().inventory.foo = n @@ -16,4 +25,3 @@ end function buildq() return tangible.build{class="login", x=10, y=0, z=0, plane="nowhere", graphic="what"} end -