From c2a94b53324b112abc3f8c157a43326ea9221c55 Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 28 Feb 2024 18:24:36 -0500 Subject: [PATCH] Do a refactor of command parsing in lpxclient and lpxserver --- luprex/cpp/core/lpxclient.cpp | 101 ++++++++---------- luprex/cpp/core/lpxserver.cpp | 88 ++++++++-------- luprex/cpp/core/luaconsole.cpp | 185 ++++++++++++++++++++------------- luprex/cpp/core/luaconsole.hpp | 43 ++++++-- luprex/cpp/core/world-core.cpp | 4 +- luprex/cpp/core/world.hpp | 2 +- 6 files changed, 240 insertions(+), 183 deletions(-) diff --git a/luprex/cpp/core/lpxclient.cpp b/luprex/cpp/core/lpxclient.cpp index cc1749fc..04187c88 100644 --- a/luprex/cpp/core/lpxclient.cpp +++ b/luprex/cpp/core/lpxclient.cpp @@ -10,7 +10,7 @@ #include -class LpxClient : public DrivenEngine { +class LpxClient : public DrivenEngine, public CommonCommands { public: using StringVec = LuaConsole::StringVec; UniqueWorld world_; @@ -137,79 +137,70 @@ public: } } - void do_luainvoke_command(const StringVec &words) { - send_invocation(Invocation(Invocation::KIND_LUA, actor_id_, actor_id_, words[1])); + virtual void do_syntax_error(std::string_view error) override { + stdostream() << "Syntax error: " << error << std::endl; } - void do_luaprobe_command(const StringVec &words) { - world_to_asynchronous(); - stdostream() << world_->probe_lua(actor_id_, words[1]); - world_to_synchronous(); + virtual void do_unknown_command(std::string_view name) override { + stdostream() << "Unknown command: " << name << std::endl; } - void do_syntax_command(const StringVec &words) { - stdostream() << "Syntax Error: " << words[1] << std::endl; + virtual void do_choose_command(int64_t index) override { + eng::string action = gui_.get_action(index); + if (action == "") { + stdostream() << "Invalid menu item #" << index << std::endl; + return; + } + send_invocation(Invocation(Invocation::KIND_CHOOSE, actor_id_, gui_.place(), action)); } - void do_view_command(const StringVec &cmd) { + virtual void do_view_command() override { stdostream() << world_->tangibles_near_debug_string(actor_id_, 1000); } - void do_menu_command(const StringVec &cmd) { + virtual void do_moveto_command(int x, int y) override { + do_unknown_command("moveto"); + } + + virtual void do_menu_command(int64_t tanid) override { world_to_asynchronous(); - int64_t place = sv::to_int64(cmd[1], actor_id_); - world_->update_gui(actor_id_, place, &gui_); + world_->update_gui(actor_id_, (tanid==0) ? actor_id_ : tanid, &gui_); stdostream() << gui_.menu_debug_string(); } - void do_choose_command(const StringVec &cmd) { - eng::string action = gui_.get_action(sv::to_int64(cmd[1])); - if (action == "") { - stdostream() << "Invalid menu item #" << std::endl; - return; - } - stdostream() << "Invoking plan: " << action << std::endl; - Invocation inv(Invocation::KIND_CHOOSE, actor_id_, gui_.place(), action); - send_invocation(inv); - } - - void do_tick_command(const util::StringVec &words) { - send_invocation(Invocation(Invocation::KIND_TICK, actor_id_, actor_id_, "")); - } - - void do_cpl_command(const util::StringVec &words) { - rescan_lua_source(); - } - - void do_work_command(const util::StringVec &words) { - // The 'work' command is a stub for sticking temporary debugging code in here. - } - - void do_quit_command(const util::StringVec &words) { + virtual void do_quit_command() override { abandon_server(); stop_driver(); } - void do_connect_command(const util::StringVec &words) { - set_initial_state_connect(util::ss("nocert:", words[1], ":8085")); + virtual void do_cpl_command() override { + rescan_lua_source(); } - void do_command(const util::StringVec &words) { - if (words.empty()) return; - 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] == "view") do_view_command(words); - 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] == "work") do_work_command(words); - else if (words[0] == "quit") do_quit_command(words); - else if (words[0] == "connect") do_connect_command(words); - else { - stdostream() << "Unsupported command: " << words[0] << std::endl; - } + virtual void do_work_command() override { + do_unknown_command("work"); + } + + virtual void do_display_command() override { + do_unknown_command("display"); + } + + virtual void do_aborthttp_command() override { + do_unknown_command("aborthttp"); + } + + virtual void do_connect_command(std::string_view hostname) override { + set_initial_state_connect(util::ss("nocert:", hostname, ":8085")); + } + + virtual void do_luainvoke_command(std::string_view cmd) override { + send_invocation(Invocation(Invocation::KIND_LUA, actor_id_, actor_id_, cmd)); + } + + virtual void do_luaprobe_command(std::string_view cmd) override { + world_to_asynchronous(); + stdostream() << world_->probe_lua(actor_id_, cmd); + world_to_synchronous(); } void change_actor_id(int64_t actor_id) { diff --git a/luprex/cpp/core/lpxserver.cpp b/luprex/cpp/core/lpxserver.cpp index 01f6fe83..9fbf3f8b 100644 --- a/luprex/cpp/core/lpxserver.cpp +++ b/luprex/cpp/core/lpxserver.cpp @@ -20,7 +20,7 @@ public: using UniqueClient = std::unique_ptr; using ClientVector = eng::vector; -class LpxServer : public DrivenEngine { +class LpxServer : public DrivenEngine, public CommonCommands { public: using StringVec = LuaConsole::StringVec; UniqueWorld master_; @@ -66,72 +66,68 @@ public: next_tick_ = 0.0; } - void do_luainvoke_command(const util::StringVec &words) { - master_->invoke(Invocation(Invocation::KIND_LUA, admin_id_, admin_id_, words[1])); + virtual void do_syntax_error(std::string_view error) override { + stdostream() << "Syntax error: " << error << std::endl; } - void do_luaprobe_command(const util::StringVec &words) { - master_->snapshot(); - stdostream() << master_->probe_lua(admin_id_, words[1]);; - master_->rollback(); + virtual void do_unknown_command(std::string_view name) override { + stdostream() << "Unknown command: " << name << std::endl; } - void do_syntax_command(const util::StringVec &words) { - stdostream() << "Syntax Error: " << words[1] << std::endl; - } - - void do_view_command(const StringVec &cmd) { - stdostream() << master_->tangibles_near_debug_string(admin_id_, 1000); - } - - void do_menu_command(const StringVec &cmd) { - int64_t place = sv::to_int64(cmd[1], admin_id_); - master_->update_gui(admin_id_, place, &gui_); - stdostream() << gui_.menu_debug_string(); - } - - void do_choose_command(const StringVec &cmd) { - stdostream() << "Chose menu item: " << cmd[1] << std::endl; - eng::string action = gui_.get_action(sv::to_int64(cmd[1])); + virtual void do_choose_command(int64_t n) override { + eng::string action = gui_.get_action(n); if (action == "") { stdostream() << "Invalid menu item #" << std::endl; return; } - stdostream() << "Invoking plan: " << action << std::endl; master_->invoke(Invocation(Invocation::KIND_CHOOSE, admin_id_, gui_.place(), action)); } - void do_tick_command(const util::StringVec &words) { - master_->invoke(Invocation(Invocation::KIND_TICK, admin_id_, admin_id_, "")); + virtual void do_view_command() override { + stdostream() << master_->tangibles_near_debug_string(admin_id_, 1000); } - void do_cpl_command(const util::StringVec &words) { - rescan_lua_source(); + virtual void do_moveto_command(int x, int y) override { + do_unknown_command("moveto"); } - void do_quit_command(const util::StringVec &words) { + virtual void do_menu_command(int64_t tanid) override { + master_->update_gui(admin_id_, (tanid == 0) ? admin_id_ : tanid, &gui_); + stdostream() << gui_.menu_debug_string(); + } + + virtual void do_quit_command() override { stop_driver(); } - void do_aborthttp_command(const util::StringVec &words) { + virtual void do_cpl_command() override { + rescan_lua_source(); + } + + virtual void do_work_command() override { + do_unknown_command("work"); + } + + virtual void do_display_command() override { + do_unknown_command("display"); + } + + virtual void do_aborthttp_command() override { master_->abort_all_http_requests(425, "http requests aborted from the server command line"); } - void do_command(const util::StringVec &words) { - if (words.empty()) return; - 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] == "view") do_view_command(words); - 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 if (words[0] == "aborthttp") do_aborthttp_command(words); - else { - stdostream() << "Unsupported command: " << words[0] << std::endl; - } + virtual void do_connect_command(std::string_view hostname) override { + do_unknown_command("connect"); + } + + virtual void do_luainvoke_command(std::string_view cmd) override { + master_->invoke(Invocation(Invocation::KIND_LUA, admin_id_, admin_id_, cmd)); + } + + virtual void do_luaprobe_command(std::string_view cmd) override { + master_->snapshot(); + stdostream() << master_->probe_lua(admin_id_, cmd); + master_->rollback(); } void delete_client(UniqueClient &client) { diff --git a/luprex/cpp/core/luaconsole.cpp b/luprex/cpp/core/luaconsole.cpp index 3e6d6f92..6cf79926 100644 --- a/luprex/cpp/core/luaconsole.cpp +++ b/luprex/cpp/core/luaconsole.cpp @@ -42,78 +42,6 @@ LuaConsole::StringVec LuaConsole::get_command() { return result; } -void LuaConsole::synerr(const eng::string &msg) { - words_.clear(); - words_.push_back("syntax"); - words_.push_back(msg); -} - -void LuaConsole::simplify(const StringVec &words) { - words_ = words; - if (words.size() == 0) { - return; - } else if (sv::valid_int64(words[0])) { - if (words.size() == 1) { - eng::string num = words[0]; - words_.clear(); - words_.push_back("choose"); - words_.push_back(num); - } else { - synerr("/choose command takes no arguments"); - } - } else if (words[0] == "choose") { - if ((words.size() == 2)&&(sv::valid_int64(words[1]))) { - // OK - } else { - synerr("/choose [menu-line-number]"); - } - } else if (words[0] == "view") { - if (words.size() != 1) { - synerr("/view takes no arguments"); - } - } else if (words[0] == "menu") { - if (words.size() == 1) { - words_.push_back("-"); - } else if ((words.size() == 2)&&(sv::valid_int64(words[1]))) { - // OK - } else { - synerr("/menu [optional-tangible-id]"); - } - } else if (words[0] == "quit") { - if (words.size() != 1) { - synerr("/quit takes no arguments"); - } - } else if (words[0] == "tick") { - if (words.size() != 1) { - synerr("/tick takes no arguments"); - } - } else if (words[0] == "cpl") { - if (words.size() != 1) { - synerr("/cpl takes no arguments"); - } - } else if (words[0] == "work") { - if (words.size() != 1) { - synerr("/work takes no arguments"); - } - } else if (words[0] == "display") { - if (words.size() != 1) { - synerr("/display takes no arguments"); - } - } else if (words[0] == "aborthttp") { - if (words.size() != 1) { - synerr("/aborthttp takes no arguments"); - } - } else if (words[0] == "connect") { - if ((words.size() == 2)&&(sv::valid_hostname(words[1]))) { - // OK - } else { - synerr("/connect [hostname]"); - } - } else { - synerr("unrecognized command"); - } -} - void LuaConsole::clear_raw_input() { raw_input_ = ""; lines_ = 0; @@ -132,7 +60,13 @@ void LuaConsole::add(eng::string line) { // Try to interpret it as a slash-command. if ((lines_ == 1)&&(raw_input_[0] == '/')) { - simplify(split_words(raw_input_.substr(1))); + words_ = split_words(raw_input_.substr(1)); + if ((words_.size() == 1) && (sv::valid_int64(words_[0]))) { + eng::string num = words_[0]; + words_.clear(); + words_.push_back("choose"); + words_.push_back(num); + } clear_raw_input(); return; } @@ -177,3 +111,108 @@ void LuaConsole::add(eng::string line) { lua_settop(lua_state_, top); } +void CommonCommands::do_command(const StringVec &words) { + if (words.size() == 0) { + return; + } + + if (words[0] == "choose") { + if ((words.size() == 2)&&(sv::valid_int64(words[1]))) { + return do_choose_command(sv::to_int64(words[1])); + } else { + return do_syntax_error("/choose [menu-line-number]"); + } + } + + if (words[0] == "view") { + if (words.size() == 1) { + return do_view_command(); + } else { + return do_syntax_error("/view takes no arguments"); + } + } + + if (words[0] == "moveto") { + if ((words.size() == 3) && sv::valid_int64(words[1]) && sv::valid_int64(words[2])) { + return do_moveto_command(sv::to_int64(words[1]), sv::to_int64(words[2])); + } else { + return do_syntax_error("/moveto [x] [y]"); + } + } + + if (words[0] == "menu") { + if (words.size() == 1) { + return do_menu_command(0); + } else if ((words.size() == 2) && sv::valid_int64(words[1])) { + return do_menu_command(sv::to_int64(words[1])); + } else { + return do_syntax_error("/menu [optional-tangible-id]"); + } + } + + if (words[0] == "quit") { + if (words.size() == 1) { + return do_quit_command(); + } else { + return do_syntax_error("/quit takes no arguments"); + } + } + + if (words[0] == "cpl") { + if (words.size() == 1) { + return do_cpl_command(); + } else { + return do_syntax_error("/cpl takes no arguments"); + } + } + + if (words[0] == "work") { + if (words.size() == 1) { + return do_work_command(); + } else { + return do_syntax_error("/work takes no arguments"); + } + } + + if (words[0] == "display") { + if (words.size() == 1) { + return do_display_command(); + } else { + return do_syntax_error("/display takes no arguments"); + } + } + + if (words[0] == "aborthttp") { + if (words.size() == 1) { + return do_aborthttp_command(); + } else { + return do_syntax_error("/aborthttp takes no arguments"); + } + } + + if (words[0] == "connect") { + if ((words.size() == 2)&&(sv::valid_hostname(words[1]))) { + return do_connect_command(words[1]); + } else { + return do_syntax_error("/connect [hostname]"); + } + } + + if (words[0] == "luainvoke") { + if (words.size() == 2) { + return do_luainvoke_command(words[1]); + } else { + return do_syntax_error("/luainvoke [command]"); + } + } + + if (words[0] == "luaprobe") { + if (words.size() == 2) { + return do_luaprobe_command(words[1]); + } else { + return do_syntax_error("/luainvoke [command]"); + } + } + + return do_unknown_command(words[0]); +} diff --git a/luprex/cpp/core/luaconsole.hpp b/luprex/cpp/core/luaconsole.hpp index 5476684f..b90256f8 100644 --- a/luprex/cpp/core/luaconsole.hpp +++ b/luprex/cpp/core/luaconsole.hpp @@ -59,8 +59,6 @@ private: StringVec words_; void clear_raw_input(); - void simplify(const StringVec &words); - void synerr(const eng::string &msg); public: LuaConsole(); @@ -74,16 +72,49 @@ public: // Get the command words. // - // Note that the command words may not be exactly what - // the user typed. Typically, the LuaConsole simplifies - // the command before returning it to the caller. - // StringVec get_command(); // Add a line of text that was just read from the console. // void add(eng::string line); +}; +////////////////////////////////////////////////////// +// +// CommonCommands is parses slash-commands that are understood by +// lpxclient and lpxserver. That way, these two separate +// sourcefiles don't have to duplicate the code for parsing. +// +// After parsing, this module calls a virtual function +// to execute the command. lpxserver and lpxclient +// implement these virtual functions in different ways. +// +////////////////////////////////////////////////////// + +class CommonCommands { +public: + using StringVec = eng::vector; + +protected: + virtual void do_syntax_error(std::string_view error) = 0; + virtual void do_unknown_command(std::string_view name) = 0; + virtual void do_choose_command(int64_t n) = 0; + virtual void do_view_command() = 0; + virtual void do_moveto_command(int x, int y) = 0; + virtual void do_menu_command(int64_t tanid) = 0; + virtual void do_quit_command() = 0; + virtual void do_cpl_command() = 0; + virtual void do_work_command() = 0; + virtual void do_display_command() = 0; + virtual void do_aborthttp_command() = 0; + virtual void do_connect_command(std::string_view hostname) = 0; + virtual void do_luainvoke_command(std::string_view cmd) = 0; + virtual void do_luaprobe_command(std::string_view cmd) = 0; + +public: + // Parse a command and call one of the virtual command functions above. + // + void do_command(const StringVec &command); }; #endif // LUACONSOLE_HPP diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index b40fe682..64afc0ad 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -268,7 +268,7 @@ int64_t World::create_login_actor(bool initialize) { return id; } -eng::string World::probe_lua(int64_t actor_id, const eng::string &lua) { +eng::string World::probe_lua(int64_t actor_id, std::string_view lua) { assert(stack_is_clear()); lua_State *L = state(); @@ -281,7 +281,7 @@ eng::string World::probe_lua(int64_t actor_id, const eng::string &lua) { LuaExtStack LS(L, closure); // create the compiled closure. - int status = luaL_loadbuffer(L, lua.c_str(), lua.size(), "=probe"); + int status = luaL_loadbuffer(L, lua.data(), lua.size(), "=probe"); lua_replace(L, closure.index()); if (status != LUA_OK) { // The closure is actually an error message. Do nothing. diff --git a/luprex/cpp/core/world.hpp b/luprex/cpp/core/world.hpp index dae42b84..a7efda42 100644 --- a/luprex/cpp/core/world.hpp +++ b/luprex/cpp/core/world.hpp @@ -200,7 +200,7 @@ public: // from the stringstream. If the lua expression returns a // value, that is also printed to the stringstream. // - eng::string probe_lua(int64_t actor_id, const eng::string &lua); + eng::string probe_lua(int64_t actor_id, std::string_view lua); // Probe the 'interface' function of the specified sprite. //