#ifndef WORLD_HPP #define WORLD_HPP #include "luastack.hpp" #include "planemap.hpp" #include "idalloc.hpp" #include "animqueue.hpp" #include "invocation.hpp" #include "streambuffer.hpp" #include "sched.hpp" #include "source.hpp" #include "gui.hpp" #include "luasnap.hpp" #include #include #include #include class World; class Tangible { private: friend class World; // Serialize and deserialize // // The tangible's ID is not serialized. When you serialize a tangible, you // should probably serialize the ID separately. // // The Lua portion of the tangible is not serialized here. Instead, the lua // portion is serialized when you serialize the lua state as a whole. // // PlaneItem is not serialized. The deserialize routine rebuilds the // PlaneItem from the AnimQueue. // // World pointer is not serialized. // void serialize(StreamBuffer *sb); void deserialize(StreamBuffer *sb); public: // Always points back to the world model. World *world_; // Animation queue. // AnimQueue anim_queue_; // Plane Item. // // The PlaneItem also contains this tangible's ID. // To move this PlaneItem, update the anim_queue first, then call // update_plane_item, which copies the data from the anim_queue. // PlaneItem plane_item_; // Player ID pool // // This is present in every tangible, whether a player or not. // However, the fifo is only enabled in logged-in players. // IdPlayerPool id_player_pool_; // constructor. // Tangible(World *w, int64_t id); // Get the ID // int64_t id() const { return plane_item_.id(); } void update_plane_item(); bool is_an_actor() { return (id_player_pool_.get_fifo_capacity() > 0); } void configure_id_pool_for_actor() { id_player_pool_.set_fifo_capacity(20); } }; class World { public: using IdVector = util::IdVector; using TanVector = std::vector; using Redirects = std::map; const float RadiusVisibility = 100.0; const float RadiusClose = 10.0; // Constructor. // // The constructor also calls 'lua_open' to create a new // lua interpreter for this world model. // World(util::WorldType wt); // Destructor. // // Not currently functional. // ~World(); // get_lua_state // // Get the lua interpreter associated with this world model. // lua_State *state() const { return lua_snap_.state(); } // get_near // // Get a list of the tangibles that are near the player. If 'exclude_nowhere' is // true, exclude any tangibles on the nowhere plane. The unsorted version returns // the tangibles in an unpredictable order. // IdVector get_near(int64_t player_id, float radius, bool exclude_nowhere) const; IdVector get_near_unsorted(int64_t player_id, float radius, bool exclude_nowhere) const; // 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. // Tangible *tangible_make(lua_State *L, int64_t id, const std::string &plane, bool pushdb); // Get the tangible ID of the specified LUA tangible database. // // Return zero if the item is not a tangible database. // static int64_t tangible_id(const LuaStack &LS, LuaSlot slot); // Get a pointer to the specified tangible. // // If there's no such tangible, returns nullptr. // Tangible *tangible_get(int64_t id); const Tangible *tangible_get(int64_t id) const; // Get a pointer to the specified tangible. // // The value on the lua stack should be a valid lua tangible. If not, // a lua error is generated. // Tangible *tangible_get(const LuaStack &LS, LuaSlot slot); // Get pointers to many tangibles. // TanVector tangible_get_all(const IdVector &ids) const; // Delete the specified tangible. // // If there's no such tangible, this is a no-op. // void tangible_delete(int64_t id); // Create a login actor. // // Creates a tangible of class 'login' and returns its ID. // This is used to create a temporary actor which is used during // the login process. // int64_t create_login_actor(); // Fetch all redirects and clear the redirects table. // Redirects fetch_redirects(); // Probe the 'interface' function of the specified sprite. // void update_gui(int64_t actor_id, int64_t place_id, Gui *gui); // Invoke an Invocation object. // // 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); // Update the source database from disk. // // Special case: if the source pointer is nullptr, does not update. // void update_source(util::LuaSourcePtr source); // Run all unit tests. // void run_unittests() { source_db_.run_unittests(); } // fetch_global_pointer // // Given a lua state, fetch the world model associated with // that lua state. // static World *fetch_global_pointer(lua_State *L); // Serialize and deserialize. // void serialize(StreamBuffer *sb); void deserialize(StreamBuffer *sb); // Snapshot and rollback. // void snapshot(); void rollback(); // Run any threads which according to the scheduler queue are ready. // void run_scheduled_threads(int64_t clk); // Check that the main thread has nothing on the stack // bool stack_is_clear() const { return lua_gettop(state()) == 0; } // Set the lthread state. // // Whenever lua code is running, and ONLY when lua code is running, // we store the following information in the world model: // // * lthread_actor_id: current actor // * lthread_place_id: current place // * lthread_use_ppool: true if we should use the player ID pool. // // As soon as the lua code stops executing, these variables are // cleared. // void set_lthread_state(int64_t actor_id, int64_t place_id, bool ppool); // Allocate a single ID. // // The rules are as follows: // * if lthread_use_ppool is false, uses the global pool. // * if lthread_actor_id is not a valid actor id, uses the global pool. // * otherwise, uses the player pool of lthread_actor_id. // int64_t alloc_id_predictable(); private: // Store a pointer to a world model into a lua registry. // static void store_global_pointer(lua_State *L, World *w); // Invoke a plan. // void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &idata); public: //////////////////////////////////////////////////////////////////////////// // // TESTING SUPPORT // // The following functions are not designed to be useful for production // code, they're designed to be helpful for unit testing. // //////////////////////////////////////////////////////////////////////////// // Add a 'walkto' animation to the specified tangible. // void tangible_walkto(int64_t id, int64_t animid, float x, float y); // Get the tangible's animation queue as a debug string. // std::string tangible_anim_debug_string(int64_t id) const; // Get a list of all existing tangibles as a comma-separated string. // std::string tangible_ids_debug_string() const; // Shows the TID (table ID) of the tables that were numbered. // TIDs are in alphabetical order. Any table without a TID // shows up as "unknown" // std::string numbered_tables_debug_string() const; // Paired tables debug string. Shows TID=TID pairs, sorted alphabetically. // std::string paired_tables_debug_string(lua_State *master) const; // Store a string in the tangible's database. // void tangible_set_string(int64_t id, const std::string &path, const std::string &value); // Copy a lua global variable into the tangible's database. // void tangible_copy_global(int64_t id, const std::string &path, const std::string &global); // Pretty-print the entire tangible database and return it as a string. // std::string tangible_pprint(int64_t id) const; // Set the tangible's lua class. // void tangible_set_class(int64_t id, const std::string &c) const; // Get the tangible's lua class (returns empty string if none). // std::string tangible_get_class(int64_t id) const; public: /////////////////////////////////////////////////////////// // // world-difftab: Nonrecursive table comparison // // These routines compare tables in the master lua to the corresponding // tables in the synchronous lua. This is a nonrecursive process, because // the recursion has already been done during the table enumeration process. // /////////////////////////////////////////////////////////// void patch_numbered_tables(StreamBuffer *sb); void diff_numbered_tables(lua_State *master, StreamBuffer *sb); void patch_tangible_databases(StreamBuffer *sb); void diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb); void patch_tangible_classes(StreamBuffer *sb); void diff_tangible_classes(const IdVector &basis, lua_State *master, StreamBuffer *sb); public: /////////////////////////////////////////////////////////// // // Difference transmission // /////////////////////////////////////////////////////////// util::IdVector get_visible_union(int64_t actor_id, World *master); int64_t patch_actor(StreamBuffer *sb); void diff_actor(int64_t actor_id, World *master, StreamBuffer *sb); void patch_visible(StreamBuffer *sb); void diff_visible(const util::IdVector &ids, World *master, StreamBuffer *sb); void patch_luatabs(StreamBuffer *sb); void diff_luatabs(int64_t actor_id, World *master, StreamBuffer *sb); void patch_tanclass(StreamBuffer *sb); void diff_tanclass(int64_t actor_id, World *master, StreamBuffer *sb); void patch_source(StreamBuffer *sb); void diff_source(World *master, StreamBuffer *sb); // This is the main entry point for difference transmission. // void patch_everything(StreamBuffer *sb); void diff_everything(int64_t actor, World *master, StreamBuffer *sb); public: /////////////////////////////////////////////////////////// // // world-pairtab: Numbering and pairing of lua tables. // // The following routines pair up tables in the synchronous // model with tables in the master model, by assigning matching // table numbers. This is not one subroutine but several, because // some of the steps happen on the server, some on the client, // and so forth. // // The goal of these routines is to build these data structures: // // Table-to-number mapping is stored in registry.tnmap // Number-to-table mapping is stored in registry.ntmap // /////////////////////////////////////////////////////////// // In the synchronous models, number tables recursively. // // This is a simple recursive traversal, which numbers tables. // This creates the initial ntmap in the synchronous models. // int number_lua_tables(const IdVector &basis); // Pair tables in the master model to tables in the synch model. // // Recursively walk the master and synchronous model in parallel, // copying table numbers from the synchronous ntmap into the master's ntmap. // void pair_lua_tables(const IdVector &basis, lua_State *master); // Number previously unpaired tables in the master model. // // This finds every not-yet-numbered table in the master model, // and appends these tables to the master's ntmap. Once they're // in the ntmap, they can be paired by simply creating new tables // in the synchronous model. // int number_remaining_tables(const IdVector &basis, lua_State *master); // Create new tables in the synchronous models. // // Creates new tables in the synchronous model and appends these // new tables to the synchronous model's ntmap. // void create_new_tables(int n); // Delete the table numbering. // // This simply removes registry.tnmap and registry.ntmap // void unnumber_lua_tables(); private: // Type of model util::WorldType world_type_; // A lua intepreter with snapshot function. // LuaSnap lua_snap_; // The Global ID Pool. // IdGlobalPool id_global_pool_; // Source Database. // SourceDB source_db_; PlaneMap plane_map_; // Tangibles table. // std::unordered_map> tangibles_; // Thread schedule: must include every thread, except // for the one currently-executing thread. // Schedule thread_sched_; // Serialized snapshot of world model. StreamBuffer snapshot_; // Redirects. // Redirects redirects_; // lthread variables: see set_lthread_state for explanation. // int64_t lthread_actor_id_; int64_t lthread_place_id_; int64_t lthread_use_ppool_; friend class Tangible; friend int lfn_tangible_animate(lua_State *L); friend int lfn_tangible_build(lua_State *L); friend int lfn_tangible_redirect(lua_State *L); friend int lfn_tangible_actor(lua_State *L); friend int lfn_tangible_place(lua_State *L); }; #endif // WORLD_HPP