From e0001127c7c387ebfbb893ede79e5a110751f4d2 Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 15 Dec 2021 14:18:19 -0500 Subject: [PATCH] Can now use /CPL to reload lua source --- luprex/core/cpp/invocation.cpp | 5 +++- luprex/core/cpp/invocation.hpp | 1 + luprex/core/cpp/lpxclient.cpp | 25 +++++++++++++++++ luprex/core/cpp/lpxserver.cpp | 8 ++++++ luprex/core/cpp/luaconsole.cpp | 4 +++ luprex/core/cpp/source.cpp | 19 +++++++++++++ luprex/core/cpp/source.hpp | 5 ++++ luprex/core/cpp/streambuffer.cpp | 5 ++++ luprex/core/cpp/streambuffer.hpp | 3 +++ luprex/core/cpp/world-core.cpp | 46 ++++++++++++++++++++++++-------- luprex/core/cpp/world.hpp | 7 ++++- 11 files changed, 115 insertions(+), 13 deletions(-) diff --git a/luprex/core/cpp/invocation.cpp b/luprex/core/cpp/invocation.cpp index 9e483b9a..f98855e1 100644 --- a/luprex/core/cpp/invocation.cpp +++ b/luprex/core/cpp/invocation.cpp @@ -64,11 +64,14 @@ std::string Invocation::debug_string() { case KIND_LUA: oss << "lua"; break; case KIND_FLUSH_PRINTS: oss << "flush_prints"; break; case KIND_TICK: oss << "tick"; break; + case KIND_LUA_SOURCE: oss << "lua_source"; break; default: oss << "UNKNOWN"; break; } oss << " a=" << actor_; oss << " p=" << place_; - oss << " " << action_; + if (kind_ != KIND_LUA_SOURCE) { + oss << " " << action_; + } for (const auto &pair : data_) { oss << " " << pair.first << "=" << pair.second; } diff --git a/luprex/core/cpp/invocation.hpp b/luprex/core/cpp/invocation.hpp index 1c7beaea..2a1540b6 100644 --- a/luprex/core/cpp/invocation.hpp +++ b/luprex/core/cpp/invocation.hpp @@ -23,6 +23,7 @@ public: KIND_LUA, KIND_FLUSH_PRINTS, KIND_TICK, + KIND_LUA_SOURCE, }; private: diff --git a/luprex/core/cpp/lpxclient.cpp b/luprex/core/cpp/lpxclient.cpp index 7483b7d1..8343d5be 100644 --- a/luprex/core/cpp/lpxclient.cpp +++ b/luprex/core/cpp/lpxclient.cpp @@ -79,6 +79,10 @@ public: // Set the console prompt get_stdio_channel()->set_prompt(console_.get_prompt()); + + // The driver loads the lua source automatically. + // However, we don't need it. Throw it out. + get_lua_source(); } void send_invocation(const Invocation &inv) { @@ -94,6 +98,14 @@ public: inv.serialize(sb); } + void send_lua_source(const util::LuaSourceVec &sv) { + StreamBuffer serial; + SourceDB::serialize_source(sv, &serial); + std::string sstr = serial.read_entire_contents(); + Invocation inv(Invocation::KIND_LUA_SOURCE, actor_id_, actor_id_, sstr); + send_invocation(inv); + } + void do_luainvoke_command(const StringVec &words) { send_invocation(Invocation(Invocation::KIND_LUA, actor_id_, actor_id_, words[1])); } @@ -134,6 +146,10 @@ public: send_invocation(Invocation(Invocation::KIND_TICK, actor_id_, actor_id_, "")); } + void do_cpl_command(const util::StringVec &words) { + rescan_lua_source(); + } + void do_quit_command(const util::StringVec &words) { abandon_server(); stop_driver(); @@ -148,6 +164,7 @@ public: else if (words[0] == "menu") do_menu_command(words); else if (words[0] == "choose") do_choose_command(words); else if (words[0] == "tick") do_tick_command(words); + else if (words[0] == "cpl") do_cpl_command(words); else if (words[0] == "quit") do_quit_command(words); else { stdostream() << "Unsupported command: " << words[0] << std::endl; @@ -214,6 +231,14 @@ public: } virtual void event_update() { + // Check for lua source code. If this returns non-null, + // it is because somebody typed CPL. + util::LuaSourcePtr lua_source = get_lua_source(); + if (lua_source != nullptr) { + send_lua_source(*lua_source); + lua_source.reset(); + } + // Check for keyboard input on stdin. while (true) { std::string line = get_stdio_channel()->in()->readline(); diff --git a/luprex/core/cpp/lpxserver.cpp b/luprex/core/cpp/lpxserver.cpp index 5306163a..5c2dc79d 100644 --- a/luprex/core/cpp/lpxserver.cpp +++ b/luprex/core/cpp/lpxserver.cpp @@ -63,6 +63,10 @@ public: master_->invoke(Invocation(Invocation::KIND_TICK, admin_id_, admin_id_, "")); } + void do_cpl_command(const util::StringVec &words) { + rescan_lua_source(); + } + void do_quit_command(const util::StringVec &words) { stop_driver(); } @@ -73,6 +77,7 @@ public: 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] == "cpl") do_cpl_command(words); else if (words[0] == "quit") do_quit_command(words); else { stdostream() << "Unsupported command: " << words[0] << std::endl; @@ -127,6 +132,9 @@ public: } virtual void event_update() { + // If the driver has reloaded the source, put it into master model. + master_->update_source(get_lua_source()); + // Check for keyboard input on stdin. while (true) { std::string line = get_stdio_channel()->in()->readline(); diff --git a/luprex/core/cpp/luaconsole.cpp b/luprex/core/cpp/luaconsole.cpp index 96274044..e8f99bbd 100644 --- a/luprex/core/cpp/luaconsole.cpp +++ b/luprex/core/cpp/luaconsole.cpp @@ -82,6 +82,10 @@ void LuaConsole::simplify(const StringVec &words) { if (words.size() != 1) { synerr("/tick takes no arguments"); } + } else if (words[0] == "cpl") { + if (words.size() != 1) { + synerr("/cpl takes no arguments"); + } } else { synerr("unrecognized command"); } diff --git a/luprex/core/cpp/source.cpp b/luprex/core/cpp/source.cpp index 8a9df96c..3b1577b4 100644 --- a/luprex/core/cpp/source.cpp +++ b/luprex/core/cpp/source.cpp @@ -446,6 +446,25 @@ void SourceDB::init(lua_State *L) { } } +void SourceDB::serialize_source(const util::LuaSourceVec &sv, StreamBuffer *sb) { + sb->write_int32(sv.size()); + for (const auto &pair : sv) { + sb->write_string(pair.first); + sb->write_string(pair.second); + } +} + +void SourceDB::deserialize_source(util::LuaSourceVec *sv, StreamBuffer *sb) { + sv->clear(); + int count = sb->read_int32(); + if ((count < 0) || (count > 10000)) throw StreamCorruption(); + for (int i = 0; i < count; i++) { + std::string fn = sb->read_string(); + std::string code = sb->read_string(); + sv->emplace_back(fn, code); + } +} + // These should go away eventually. They're for debugging. LuaDefine(coroutine_setnextid, "c") { LuaArg co, lid; diff --git a/luprex/core/cpp/source.hpp b/luprex/core/cpp/source.hpp index 47f439bb..ed7ab57d 100644 --- a/luprex/core/cpp/source.hpp +++ b/luprex/core/cpp/source.hpp @@ -172,6 +172,11 @@ public: // void set(const std::string &fn, const std::string &code, int sequence); std::string get(const std::string &fn); + + // Serialize and unserialize a source vector. + // + static void serialize_source(const util::LuaSourceVec &sv, StreamBuffer *sb); + static void deserialize_source(util::LuaSourceVec *sv, StreamBuffer *sb); }; #endif // SOURCE_HPP diff --git a/luprex/core/cpp/streambuffer.cpp b/luprex/core/cpp/streambuffer.cpp index 8e9313c8..b6849913 100644 --- a/luprex/core/cpp/streambuffer.cpp +++ b/luprex/core/cpp/streambuffer.cpp @@ -30,6 +30,11 @@ StreamBuffer::StreamBuffer(const char *s, int64_t size) { write_cursor_ = buf_hi_; } +StreamBuffer::StreamBuffer(const std::string &src) { + init(true, false, const_cast(src.c_str()), src.size()); + write_cursor_ = buf_hi_; +} + StreamBuffer::~StreamBuffer() { if (owned_ && (buf_lo_ != 0)) free(buf_lo_); } diff --git a/luprex/core/cpp/streambuffer.hpp b/luprex/core/cpp/streambuffer.hpp index 6b647f2a..d5accff4 100644 --- a/luprex/core/cpp/streambuffer.hpp +++ b/luprex/core/cpp/streambuffer.hpp @@ -249,6 +249,9 @@ public: // Construct a streambuffer that reads from an external block of bytes. StreamBuffer(const char *s, int64_t len); + // Construct a streambuffer that reads from an external block of bytes. + StreamBuffer(const std::string &data); + // Delete a StreamBuffer. ~StreamBuffer(); diff --git a/luprex/core/cpp/world-core.cpp b/luprex/core/cpp/world-core.cpp index 420b5d2d..e361d626 100644 --- a/luprex/core/cpp/world-core.cpp +++ b/luprex/core/cpp/world-core.cpp @@ -375,16 +375,20 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) { } void World::update_source(const util::LuaSourcePtr &source) { - assert(stack_is_clear()); if (source != nullptr) { - source_db_.update(*source); - assert(stack_is_clear()); - std::string errs = source_db_.rebuild(); - // I don't have a good place to send the error messages right - // now. The engine needs a catch-all place to send errors that - // occur at unexpected times. - std::cerr << errs; + update_source(*source); } +} + +void World::update_source(const util::LuaSourceVec &source) { + assert(stack_is_clear()); + source_db_.update(source); + assert(stack_is_clear()); + std::string errs = source_db_.rebuild(); + // I don't have a good place to send the error messages right + // now. The engine needs a catch-all place to send errors that + // occur at unexpected times. + std::cerr << errs; assert(stack_is_clear()); } @@ -407,6 +411,10 @@ void World::invoke(const Invocation &inv) { break; case Invocation::KIND_TICK: invoke_tick(inv.actor(), inv.place(), inv.action(), inv.data()); + break; + case Invocation::KIND_LUA_SOURCE: + invoke_lua_source(inv.actor(), inv.place(), inv.action(), inv.data()); + break; default: // Do nothing. Standard behavior for any invalid command is to // simply do nothing at all. Perhaps eventually we may add a flag @@ -604,9 +612,25 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a } 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(); + if (!util::world_type_authoritative(world_type_)) { + return; + } + clock_ += 1; + run_scheduled_threads(); +} + +void World::invoke_lua_source(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data) { + if (!util::world_type_authoritative(world_type_)) { + return; + } + // We need some kind of authentication here. + try { + StreamBuffer sb(action); + util::LuaSourceVec sv; + SourceDB::deserialize_source(&sv, &sb); + update_source(sv); + } catch (const StreamException &ex) { + return; } } diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index 2f0963f3..70d827f6 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -196,7 +196,8 @@ public: // Special case: if the source pointer is nullptr, does not update. // void update_source(const util::LuaSourcePtr &source); - + void update_source(const util::LuaSourceVec &source); + // Run all unit tests. // void run_unittests(); @@ -277,6 +278,10 @@ private: // void invoke_tick(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data); + // Invoke the lua_source operation. + // + void invoke_lua_source(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &data); + public: //////////////////////////////////////////////////////////////////////////// //