#ifndef WORLD_HPP #define WORLD_HPP #include "wrap-set.hpp" #include "wrap-unordered-map.hpp" #include "wrap-map.hpp" #include #include #include "luastack.hpp" #include "planemap.hpp" #include "idalloc.hpp" #include "animqueue.hpp" #include "invocation.hpp" #include "streambuffer.hpp" #include "debugcollector.hpp" #include "printbuffer.hpp" #include "sched.hpp" #include "http.hpp" #include "source.hpp" #include "gui.hpp" #include "luasnap.hpp" class World; class Tangible : public eng::opnew { 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_; // Print Buffer // // 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_; // 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(3); id_player_pool_.refill(); } }; using UniqueTangible = std::unique_ptr; class World : public eng::opnew { public: using IdVector = util::IdVector; using TanVector = eng::vector; using Redirects = eng::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 (but still include the player himself). // The unsorted version returns the tangibles in an unpredictable order. // IdVector get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player) const; IdVector get_near_unsorted(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player) const; // Make a tangible. // // 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 eng::string &plane, bool pushdb); // 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 an arbitrary lua expression. // // Any print-statements in the lua code are sent into // a stringstream. The return value of probe_lua is the string // 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); // 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. // // It is legal to mutate a world model without using 'Invoke', but // only in authoritative world models. // void invoke(const Invocation &inv); // Get the PrintBuffer of the actor. // const PrintBuffer *get_printbuffer(int64_t actor_id); // Update the source database from disk. // // 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); // Supply an HTTP response to an outstanding HTTP request. void http_response(const HttpClientResponse &response); void http_responses(const HttpClientResponseVec &responses); // Abort all HTTP requests. This is typically used after // reloading a world from a save-game. The http requests that // were in progress are long-since dead. void abort_all_http_requests(int status_code, std::string_view error); // Run all unit tests. // void 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); // Check if the world is authoritative. // bool is_authoritative() const { return (world_type_ == util::WORLD_TYPE_MASTER) || (world_type_ == util::WORLD_TYPE_STANDALONE); } // Get a table showing all outstanding HTTP requests. // const HttpClientRequestMap &http_requests() const { return http_requests_; } // Serialize and deserialize. // void serialize(StreamBuffer *sb); void deserialize(StreamBuffer *sb); // Snapshot and rollback. // void snapshot(); void rollback(); bool snapshot_empty() { return snapshot_.empty(); } // Run any threads which according to the scheduler queue are ready. // void run_scheduled_threads(); // 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. // * lthread_prints_: a stringstream which will collect 'print' statements. // // As soon as the lua code stops executing, these variables are // cleared. // void clear_lthread_state(); void open_lthread_state(int64_t actor_id, int64_t place_id, int64_t thread_id, bool ppool, bool prints); void close_lthread_state(); std::ostream *lthread_print_stream() const; // 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(); // If we're in a probe, generate an error. // If we're in a nonauthoritative model, do a nopredict yield. // Otherwise, return. void guard_blockable(lua_State *L, const char *fn); // If we're in a probe, return. // If we're in a nonauthoritative model, do a nopredict yield. // Otherwise, return. void guard_nopredict(lua_State *L, const char *fn); private: // Add a thread to the scheduler queue. // void schedule(int64_t clk, int64_t thid, int64_t plid); // Store a pointer to a world model into a lua registry. // static void store_global_pointer(lua_State *L, World *w); // Start a thread on the specified tangible. void tangible_start(Tangible *actor, Tangible *place, LuaStack &LS, LuaSlot func, int argpos, int nargs); // Invoke a plan. // void invoke_plan(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data); // Invoke a lua string. // void invoke_lua(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data); // Invoke the flush-prints operation. // void invoke_flush_prints(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data); // Invoke the tick operation. // void invoke_tick(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data); // Invoke the lua_source operation. // void invoke_lua_source(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data); 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. // eng::string tangible_anim_debug_string(int64_t id) const; // Get the tangible's ID Pool as a debug string. // eng::string tangible_id_pool_debug_string(int64_t id) const; // Get a list of all existing tangibles as a comma-separated string. // eng::string tangible_ids_debug_string() const; // Get a list of all tangibles near the target as a string. // eng::string tangibles_near_debug_string(int64_t actor, int64_t distance); // 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" // eng::string numbered_tables_debug_string() const; // Paired tables debug string. Shows TID=TID pairs, sorted alphabetically. // eng::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 eng::string &path, const eng::string &value); // Copy a lua global variable into the tangible's database. // void tangible_copy_global(int64_t id, const eng::string &path, const eng::string &global); // Pretty-print the entire tangible database and return it as a string. // eng::string tangible_pprint(int64_t id) const; // Set the tangible's lua class. // void tangible_set_class(int64_t id, const eng::string &c) const; // Get the tangible's lua class (returns empty string if none). // eng::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, DebugCollector *dbc); void diff_numbered_tables(lua_State *master, StreamBuffer *sb); void patch_tangible_databases(StreamBuffer *sb, DebugCollector *dbc); void diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb); void patch_tangible_classes(StreamBuffer *sb, DebugCollector *dbc); 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, DebugCollector *dbc); void diff_actor(int64_t actor_id, World *master, StreamBuffer *sb); void patch_visible(StreamBuffer *sb, DebugCollector *dbc); void diff_visible(const util::IdVector &ids, World *master, StreamBuffer *sb); void patch_luatabs(StreamBuffer *sb, DebugCollector *dbc); void diff_luatabs(int64_t actor_id, World *master, StreamBuffer *sb); void patch_tanclass(StreamBuffer *sb, DebugCollector *dbc); void diff_tanclass(int64_t actor_id, World *master, StreamBuffer *sb); void patch_source(StreamBuffer *sb, DebugCollector *dbc); void diff_source(World *master, StreamBuffer *sb); // This is the main entry point for difference transmission. // int64_t patch_everything(StreamBuffer *sb, DebugCollector *dbc); 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. // eng::unordered_map tangibles_; // Current time. // int64_t clock_; // Thread schedule: must include every thread, except // for the one currently-executing thread. // Schedule thread_sched_; // Outstanding HTTP requests, indexed by request ID. // Authoritative models only. // HttpClientRequestMap http_requests_; // 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_thread_id_; int64_t lthread_use_ppool_; std::unique_ptr lthread_prints_; 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); friend int lfn_tangible_nopredict(lua_State *L); friend int lfn_tangible_near(lua_State *L); friend int lfn_tangible_scan(lua_State *L); friend int lfn_tangible_start(lua_State *L); friend int lfn_math_random(lua_State *L); friend int lfn_math_randomstate(lua_State *L); friend int lfn_wait(lua_State *L); friend int lfn_nopredict(lua_State *L); friend int lfn_http_get(lua_State *L); }; using UniqueWorld = std::unique_ptr; #endif // WORLD_HPP