From 92f7d8cd91c775c18bc190513e00a96abde41eff Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Mon, 19 Jul 2021 15:32:34 -0400 Subject: [PATCH] Plan invocations are now serializable objects --- luprex/core/Makefile | 1 + luprex/core/cpp/gui.cpp | 1 + luprex/core/cpp/gui.hpp | 2 +- luprex/core/cpp/invocation.cpp | 44 ++++++++++++++++++++++++++++++ luprex/core/cpp/invocation.hpp | 47 ++++++++++++++++++++++++++++++++ luprex/core/cpp/streambuffer.hpp | 2 +- luprex/core/cpp/textgame.cpp | 6 ++-- luprex/core/cpp/world.cpp | 31 +++++++++++++++------ luprex/core/cpp/world.hpp | 14 ++++++++-- 9 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 luprex/core/cpp/invocation.cpp create mode 100644 luprex/core/cpp/invocation.hpp diff --git a/luprex/core/Makefile b/luprex/core/Makefile index 89897de3..4a56e48d 100644 --- a/luprex/core/Makefile +++ b/luprex/core/Makefile @@ -2,6 +2,7 @@ CXX=g++ -std=c++17 -Wall -g -Iinc -Icpp CPP_FILES=\ + cpp/invocation.cpp\ cpp/spookyv2.cpp\ cpp/util.cpp\ cpp/luastack.cpp\ diff --git a/luprex/core/cpp/gui.cpp b/luprex/core/cpp/gui.cpp index 472203e3..4e037023 100644 --- a/luprex/core/cpp/gui.cpp +++ b/luprex/core/cpp/gui.cpp @@ -35,6 +35,7 @@ bool Gui::has_action(const std::string &action) const { return false; } + LuaDefine(gui_menu_item, "c") { Gui *gui = Gui::fetch_global_pointer(L); LuaArg laction, llabel; diff --git a/luprex/core/cpp/gui.hpp b/luprex/core/cpp/gui.hpp index 57613f3d..91881350 100644 --- a/luprex/core/cpp/gui.hpp +++ b/luprex/core/cpp/gui.hpp @@ -5,6 +5,7 @@ #include #include #include "luastack.hpp" +#include "streambuffer.hpp" class GuiElt { friend class Gui; @@ -45,7 +46,6 @@ public: static Gui *fetch_global_pointer(lua_State *L); }; -using GuiResult = std::map; #endif // GUI_HPP diff --git a/luprex/core/cpp/invocation.cpp b/luprex/core/cpp/invocation.cpp new file mode 100644 index 00000000..c79ffb4d --- /dev/null +++ b/luprex/core/cpp/invocation.cpp @@ -0,0 +1,44 @@ + +#include "invocation.hpp" + + +void InvocationData::serialize(StreamBuffer *sb) const { + assert(int(size()) < 65536); + sb->write_uint16(size()); + for (const auto &pair : *this) { + sb->write_string(pair.first); + sb->write_string(pair.second); + } +} + +void InvocationData::deserialize(StreamBuffer *sb) { + clear(); + int size = sb->read_uint16(); + for (int i = 0; i < size; i++) { + std::string key = sb->read_string(); + std::string val = sb->read_string(); + (*this)[key] = val; + } +} + +Invocation::Invocation() : kind_(KIND_INVALID), actor_(0), place_(0) {} + +Invocation::Invocation(Kind kind, int64_t actor, int64_t place, const std::string &action, const InvocationData &data) + : kind_(kind), actor_(actor), place_(place), action_(action), data_(data) {} + +void Invocation::serialize(StreamBuffer *sb) const { + sb->write_uint8(kind_); + sb->write_int64(actor_); + sb->write_int64(place_); + sb->write_string(action_); + data_.serialize(sb); +} + +void Invocation::deserialize(StreamBuffer *sb) { + kind_ = Kind(sb->read_uint8()); + actor_ = sb->read_int64(); + place_ = sb->read_int64(); + action_ = sb->read_string(); + data_.deserialize(sb); +} + diff --git a/luprex/core/cpp/invocation.hpp b/luprex/core/cpp/invocation.hpp new file mode 100644 index 00000000..c05908e8 --- /dev/null +++ b/luprex/core/cpp/invocation.hpp @@ -0,0 +1,47 @@ +#ifndef INVOCATION_HPP +#define INVOCATION_HPP + +#include +#include +#include +#include "streambuffer.hpp" + + +class InvocationData : public std::map { +public: + void serialize(StreamBuffer *sb) const; + void deserialize(StreamBuffer *sb); +}; + +class Invocation { +public: + enum Kind { + KIND_INVALID, + KIND_PLAN, + }; + +private: + Kind kind_; + int64_t actor_; + int64_t place_; + std::string action_; + InvocationData data_; +public: + Invocation(); + Invocation(Kind kind, int64_t actor, int64_t place, const std::string &action, const InvocationData &data); + + Kind kind() const { return kind_; } + int64_t actor() const { return actor_; } + int64_t place() const { return place_; } + const std::string &action() const { return action_; } + const InvocationData &data() const { return data_; } + + void serialize(StreamBuffer *sb) const; + void deserialize(StreamBuffer *sb); +}; + +class InvocationQueue : public std::deque { +}; + + +#endif // INVOCATION_HPP diff --git a/luprex/core/cpp/streambuffer.hpp b/luprex/core/cpp/streambuffer.hpp index 4b987606..b97c65e8 100644 --- a/luprex/core/cpp/streambuffer.hpp +++ b/luprex/core/cpp/streambuffer.hpp @@ -353,7 +353,7 @@ public: uint16_t read_uint16() { return read_int16(); } uint32_t read_uint32() { return read_int32(); } uint64_t read_uint64() { return read_int64(); } - bool read_bool() { return read_int8(); } + bool read_bool() { return read_int8(); } // May throw StreamEof or StreamCorruption. size_t read_size(); diff --git a/luprex/core/cpp/textgame.cpp b/luprex/core/cpp/textgame.cpp index b60761d6..5e3cbb61 100644 --- a/luprex/core/cpp/textgame.cpp +++ b/luprex/core/cpp/textgame.cpp @@ -9,6 +9,7 @@ #include "luastack.hpp" #include "util.hpp" #include "gui.hpp" +#include "invocation.hpp" #include "world.hpp" #include "traceback.hpp" #include "textgame.hpp" @@ -122,10 +123,11 @@ void TextGame::do_choose_command(const StringVec &cmd) { } std::string action = elts[index].action(); std::cerr << "Invoking plan: " << action << std::endl; - GuiResult dummyresult; + InvocationData dummyresult; dummyresult["flavor"] = "chocolate"; dummyresult["color"] = "blue"; - world_->invoke_plan(actor_id_, gui_place_, action, dummyresult); + Invocation inv(Invocation::KIND_PLAN, actor_id_, gui_place_, action, dummyresult); + world_->invoke(inv); } void TextGame::do_snapshot_command(const StringVec &cmd) { diff --git a/luprex/core/cpp/world.cpp b/luprex/core/cpp/world.cpp index 721deb50..c7e340b4 100644 --- a/luprex/core/cpp/world.cpp +++ b/luprex/core/cpp/world.cpp @@ -265,7 +265,22 @@ void World::update_gui(int64_t actor_id, int64_t place_id, Gui *gui) { assert(stack_is_clear()); } -void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const GuiResult &gres) { +void World::invoke(const Invocation &inv) { + switch (inv.kind()) { + case Invocation::KIND_PLAN: + invoke_plan(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 + // to the world model to indicate that we've detected an invalid + // command, to allow us to close the connection to a client that + // is misbehaving. + break; + } +} + +void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &idata) { assert(stack_is_clear()); // Validate that the action is legal. @@ -288,8 +303,8 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a // Set up for Lua manipulation. lua_State *L = state(); - LuaVar actor, place, func, tangibles, mt, index, actions, thread, threads, message, guires; - LuaStack LS(L, actor, place, func, tangibles, mt, index, actions, thread, threads, message, guires); + LuaVar actor, place, func, tangibles, mt, index, actions, thread, threads, message, invdata; + LuaStack LS(L, actor, place, func, tangibles, mt, index, actions, thread, threads, message, invdata); // Get the actor and place. LS.rawget(tangibles, LuaRegistry, "tangibles"); @@ -322,10 +337,10 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a return; } - // Convert the GuiResult into a lua table. - LS.newtable(guires); - for (const auto &p : gres) { - LS.rawset(guires, p.first, p.second); + // Convert the InvocationData into a lua table. + LS.newtable(invdata); + for (const auto &p : idata) { + LS.rawset(invdata, p.first, p.second); } // Create a new thread, set up function and parameters. @@ -333,7 +348,7 @@ void World::invoke_plan(int64_t actor_id, int64_t place_id, const std::string &a lua_pushvalue(L, func.index()); lua_pushvalue(L, actor.index()); lua_pushvalue(L, place.index()); - lua_pushvalue(L, guires.index()); + lua_pushvalue(L, invdata.index()); lua_xmove(L, CO, 4); // Store the thread into place's thread table. diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index f0ce6468..6b06c717 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -6,6 +6,7 @@ #include "planemap.hpp" #include "idalloc.hpp" #include "animqueue.hpp" +#include "invocation.hpp" #include "sched.hpp" #include "source.hpp" #include "gui.hpp" @@ -113,6 +114,10 @@ public: // Check if main thread has nothing on the stack bool stack_is_clear() const { return lua_gettop(state()) == 0; } + + // Invoke an action plan. + // + void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &idata); public: // Constructor. // @@ -181,10 +186,13 @@ public: // void update_gui(int64_t actor_id, int64_t place_id, Gui *gui); - // Invoke an action plan. + // Invoke an Invocation object. // - void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const GuiResult &gres); - + // This is the primary dispatcher for all operations that mutate a world model. + // To mutate a world model, create an invocation, then invoke it. + // + void invoke(const Invocation &inv); + // fetch_global_pointer // // Given a lua state, fetch the world model associated with