diff --git a/luprex/cpp/core/animqueue.cpp b/luprex/cpp/core/animqueue.cpp index 76c773b0..4a19fe5a 100644 --- a/luprex/cpp/core/animqueue.cpp +++ b/luprex/cpp/core/animqueue.cpp @@ -323,8 +323,8 @@ bool AnimStep::from_string(const eng::string &config) { return true; } -AnimQueue::AnimQueue(util::WorldType wt) { - version_autoinc_ = (wt == util::WORLD_TYPE_MASTER); +AnimQueue::AnimQueue(WorldType wt) { + version_autoinc_ = (wt == WORLD_TYPE_MASTER); size_limit_ = 10; // Default size limit. steps_.emplace_back(); steps_.front().keep_state_only(); @@ -576,8 +576,8 @@ static bool diff_works(const AnimQueue &master, AnimQueue &sync) { LuaDefine(unittests_animqueue, "", "some unit tests") { // Useful objects. AnimStep stp; - AnimQueue aq(util::WORLD_TYPE_MASTER); - AnimQueue aqds(util::WORLD_TYPE_S_SYNC); + AnimQueue aq(WORLD_TYPE_MASTER); + AnimQueue aqds(WORLD_TYPE_PREDICTIVE); StreamBuffer sb; // Debug string of a newly initialized queue diff --git a/luprex/cpp/core/animqueue.hpp b/luprex/cpp/core/animqueue.hpp index f3aaf94f..e9d16433 100644 --- a/luprex/cpp/core/animqueue.hpp +++ b/luprex/cpp/core/animqueue.hpp @@ -169,7 +169,7 @@ private: class AnimQueue : public eng::nevernew { public: // World type determines whether versions increment or autozero - AnimQueue(util::WorldType wt); + AnimQueue(WorldType wt); // Simple getters. const AnimStep &nth(int n) const { return steps_[n]; } diff --git a/luprex/cpp/core/eng-tests.cpp b/luprex/cpp/core/eng-tests.cpp index f21186a7..e4f17ba5 100644 --- a/luprex/cpp/core/eng-tests.cpp +++ b/luprex/cpp/core/eng-tests.cpp @@ -114,7 +114,7 @@ private: void event_init(int argc, char *argv[]) { - world_.reset(new World(util::WORLD_TYPE_STANDALONE)); + world_.reset(new World(WORLD_TYPE_MASTER)); world_->update_source(get_lua_source()); world_->run_unittests(); stop_driver(); diff --git a/luprex/cpp/core/globaldb.cpp b/luprex/cpp/core/globaldb.cpp index d90367f1..e2de6a1a 100644 --- a/luprex/cpp/core/globaldb.cpp +++ b/luprex/cpp/core/globaldb.cpp @@ -7,6 +7,8 @@ LuaDefine(global_once, "name", "for a given string, returns true exactly once") LuaVar oncedb, val; LuaStack LS(L, name, flag, oncedb, val); + LS.guard_nopredict("global.once"); + // Get a pointer to the oncedb. LS.rawget(oncedb, LuaRegistry, "oncedb"); if (!LS.istable(oncedb)) { @@ -25,11 +27,14 @@ LuaDefine(global_once, "name", "for a given string, returns true exactly once") return LS.result(); } + LuaDefine(global_clearonce, "name", "reset the specified once-flag") { LuaArg name; LuaVar oncedb; LuaStack LS(L, name, oncedb); + LS.guard_nopredict("global.clearonce"); + // Get a pointer to the oncedb. LS.rawget(oncedb, LuaRegistry, "oncedb"); if (!LS.istable(oncedb)) { @@ -50,11 +55,6 @@ LuaDefine(global_table, "globalname", "get a table where global data can be stor // Get a pointer to the globaldb. LS.rawget(globaldb, LuaRegistry, "globaldb"); - // nopredict - if (lua_isyieldable(L) && (!LS.istable(globaldb))) { - return lua_yield(L, 0); - } - // Get the globaltab from the globaldb, sanity check it. LS.rawget(globaltab, globaldb, globalname); if (LS.istable(globaltab)) { diff --git a/luprex/cpp/core/lpxclient.cpp b/luprex/cpp/core/lpxclient.cpp index 811d97eb..02d1d345 100644 --- a/luprex/cpp/core/lpxclient.cpp +++ b/luprex/cpp/core/lpxclient.cpp @@ -24,7 +24,7 @@ public: public: void set_initial_state() { // Create the world model. - world_.reset(new World(util::WORLD_TYPE_C_SYNC)); + world_.reset(new World(WORLD_TYPE_PREDICTIVE)); // This is a temporary actor that will be used only until the server sends // us the first difference transmission. We do this only to establish diff --git a/luprex/cpp/core/lpxserver.cpp b/luprex/cpp/core/lpxserver.cpp index 27dffcc4..9bdec15b 100644 --- a/luprex/cpp/core/lpxserver.cpp +++ b/luprex/cpp/core/lpxserver.cpp @@ -33,7 +33,7 @@ public: public: virtual void event_init(int argc, char *argv[]) { // Create the master world model. - master_.reset(new World(util::WORLD_TYPE_MASTER)); + master_.reset(new World(WORLD_TYPE_MASTER)); // Update the source code of the master model. master_->update_source(get_lua_source()); @@ -189,7 +189,7 @@ public: Client *client = new Client; client->actor_id_ = master_->create_login_actor(); client->channel_ = std::move(chan); - client->sync_.reset(new World(util::WORLD_TYPE_S_SYNC)); + client->sync_.reset(new World(WORLD_TYPE_PREDICTIVE)); client->sync_->create_login_actor(); clients_.emplace_back(client); stdostream() << "New client: actor id=" << client->actor_id_ << std::endl; diff --git a/luprex/cpp/core/luasnap.cpp b/luprex/cpp/core/luasnap.cpp index beb39d59..ae307e71 100644 --- a/luprex/cpp/core/luasnap.cpp +++ b/luprex/cpp/core/luasnap.cpp @@ -85,7 +85,8 @@ void LuaSnap::deserialize(StreamBuffer *sb) { void *ud = sb->lua_reader_ud(len); // Call eris with the permanents table and passing the snapshot as a lua_Reader. - lua_getfield(state_, LUA_REGISTRYINDEX, "unpersist"); + lua_pushstring(state_, "unpersist"); + lua_rawget(state_, LUA_REGISTRYINDEX); eris_undump(state_, sb->lua_reader, ud); assert(lua_gettop(state_) == 2); diff --git a/luprex/cpp/core/luastack.cpp b/luprex/cpp/core/luastack.cpp index 919e9301..1f30cf7b 100644 --- a/luprex/cpp/core/luastack.cpp +++ b/luprex/cpp/core/luastack.cpp @@ -466,6 +466,24 @@ void LuaStack::setvisited(LuaSlot tab, bool visited) const { lua_modflagbits(L_, tab.index(), 0x0010, visited ? 0x0010 : 0); } +WorldType LuaStack::world_type() const { + lua_pushstring(L_, "worldtype"); + lua_rawget(L_, LUA_REGISTRYINDEX); + lua_Integer n = lua_tointeger(L_, -1); + lua_pop(L_, 1); + assert(n != 0); + return (WorldType)n; +} + +void LuaStack::guard_nopredict(const char *fn) { + if (lua_isyieldable(L_)) { + if (!is_authoritative()) { + lua_yield(L_, 0); + luaL_error(L_, "unexplained nopredict failure in %s", fn); + } + } +} + LuaKeywordParser::LuaKeywordParser(lua_State *L, int slot) { L_ = L; slot_ = slot; diff --git a/luprex/cpp/core/luastack.hpp b/luprex/cpp/core/luastack.hpp index 6600e14e..2460514f 100644 --- a/luprex/cpp/core/luastack.hpp +++ b/luprex/cpp/core/luastack.hpp @@ -226,6 +226,13 @@ int LuaTypeTagValue(lua_State *L) { return 0; } #define LUA_TT_GLOBALDB 22 #define LUA_TT_CLASS 23 +// World types enum. + +enum WorldType { + WORLD_TYPE_MASTER = 1, + WORLD_TYPE_PREDICTIVE = 2, +}; + // We use lightuserdata to store 'tokens': short // strings of 8 characters or less. These tokens // are useful as unique markers. The 8 characters @@ -517,6 +524,17 @@ public: bool getvisited(LuaSlot tab) const; void setvisited(LuaSlot tab, bool visited) const; + // Return the world type (from the registry). + WorldType world_type() const; + + // World types that are authoritative. + static bool is_authoritative(WorldType t) { return (t == WORLD_TYPE_MASTER); } + bool is_authoritative() { return is_authoritative(world_type()); } + + // Stop execution of this thread if in a nonauth model, + // and if the thread is not a probe. + void guard_nopredict(const char *fn); + // Return true if the int64 value can be stored as a lua number. static bool int64_storable(int64_t v) { return (v <= MAXINT) && (v >= -MAXINT); } }; diff --git a/luprex/cpp/core/textgame.cpp b/luprex/cpp/core/textgame.cpp index cfac55c2..7fe96480 100644 --- a/luprex/cpp/core/textgame.cpp +++ b/luprex/cpp/core/textgame.cpp @@ -98,7 +98,7 @@ private: void event_init(int argc, char *argv[]) { - world_.reset(new World(util::WORLD_TYPE_STANDALONE)); + world_.reset(new World(WORLD_TYPE_MASTER)); world_->update_source(get_lua_source()); world_->run_unittests(); actor_id_ = world_->create_login_actor(); diff --git a/luprex/cpp/core/util.hpp b/luprex/cpp/core/util.hpp index a8fb5a7c..55b57f9f 100644 --- a/luprex/cpp/core/util.hpp +++ b/luprex/cpp/core/util.hpp @@ -198,13 +198,6 @@ bool valid_number(string_view v, bool plus, bool minus, bool dec, bool exp); namespace util { -enum WorldType { - WORLD_TYPE_STANDALONE, - WORLD_TYPE_C_SYNC, - WORLD_TYPE_S_SYNC, - WORLD_TYPE_MASTER, -}; - enum MessageType { MSG_NULL, MSG_DIFF, diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index 64122f91..87e5e676 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -29,12 +29,12 @@ World *World::fetch_global_pointer(lua_State *L) { World::~World() { } -World::World(util::WorldType wt) { +World::World(WorldType wt) { // Master world model by default. world_type_ = wt; // Initialize the ID allocator in master mode. - if (wt == util::WORLD_TYPE_MASTER || wt == util::WORLD_TYPE_STANDALONE) { + if (is_authoritative()) { id_global_pool_.init_master(); } else { id_global_pool_.init_synch(); @@ -63,11 +63,12 @@ World::World(util::WorldType wt) { // Create the tangibles table in the registry. LS.rawset(LuaRegistry, "tangibles", LuaNewTable); + // Store the world type in the registry. + LS.rawset(LuaRegistry, "worldtype", wt); + // Create the globaldb and oncedb in the registry. - if ((wt == util::WORLD_TYPE_MASTER) || (wt == util::WORLD_TYPE_STANDALONE)) { - LS.rawset(LuaRegistry, "globaldb", LuaNewTable); - LS.rawset(LuaRegistry, "oncedb", LuaNewTable); - } + LS.rawset(LuaRegistry, "globaldb", LuaNewTable); + LS.rawset(LuaRegistry, "oncedb", LuaNewTable); // Initialize the SourceDB. At this stage, the sourcedb is // empty, so it's just populating the lua builtins. @@ -788,12 +789,12 @@ void World::invoke_lua_source(int64_t actor_id, int64_t place_id, const eng::str void World::guard_blockable(lua_State *L, const char *fn) { if (lthread_thread_id_ == 0) { - // in a probe, http.get throws an error. + // in a probe, blocking functions like http.get throw an error. luaL_error(L, "cannot %s in a probe", fn); assert(false); } if (!is_authoritative()) { - // in a nonauth model, http.get is converted to nopredict. + // in a nonauth model, blocking functions like http.get are converted to nopredict. lua_yield(L, 0); luaL_error(L, "unexplained nopredict failure in %s", fn); assert(false); @@ -801,6 +802,8 @@ void World::guard_blockable(lua_State *L, const char *fn) { } void World::guard_nopredict(lua_State *L, const char *fn) { + // Caution: this code must be equivalent to the + // code in LuaStack::guard_nopredict. if (lthread_thread_id_ == 0) { return; } @@ -883,7 +886,10 @@ void World::run_scheduled_threads() { LS.rawset(thinfo, "isnew", false); LS.rawset(thinfo, "useppool", false); } else { - // In a nonauth model, a yield is converted to a 'nopredict'. + // When a nonauthoritative model yields, for any reason, + // the thread is discarded. This is also used as a way to implement + // nopredict: the thread that wants to 'nopredict' just yields, + // knowing that this will cause it to be killed. LS.rawset(threads, sched.thread_id(), LuaNil); } } else { diff --git a/luprex/cpp/core/world-pairtab.cpp b/luprex/cpp/core/world-pairtab.cpp index f6aab82a..fc1b829b 100644 --- a/luprex/cpp/core/world-pairtab.cpp +++ b/luprex/cpp/core/world-pairtab.cpp @@ -136,8 +136,6 @@ void World::pair_lua_tables(const IdVector &basis, lua_State *master) { // If the master table is already paired, skip. MLS.rawget(midx, mtnmap, mtab); if (MLS.isnumber(midx)) continue; - // If the synch table is not a table, skip. - if (!SLS.istable(stab)) continue; // If the synch table doesn't have a number, skip. SLS.rawget(sidx, stnmap, stab); if (!SLS.isnumber(sidx)) continue; diff --git a/luprex/cpp/core/world-testing.cpp b/luprex/cpp/core/world-testing.cpp index 0d777d08..889fb748 100644 --- a/luprex/cpp/core/world-testing.cpp +++ b/luprex/cpp/core/world-testing.cpp @@ -241,9 +241,9 @@ static bool worlds_identical(const UniqueWorld &w1, const UniqueWorld &w2) { } LuaDefine(unittests_world1animdiff, "", "some unit tests") { - UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); - UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); - UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC)); + UniqueWorld m(new World(WORLD_TYPE_MASTER)); + UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE)); + UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE)); StreamBuffer sb; util::IdVector ids = util::id_vector_create(123, 345); @@ -311,8 +311,8 @@ LuaDefine(unittests_world1animdiff, "", "some unit tests") { } LuaDefine(unittests_world2pairtab, "", "some unit tests") { - UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); - UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); + UniqueWorld m(new World(WORLD_TYPE_MASTER)); + UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE)); StreamBuffer sb; int ncreate; @@ -359,9 +359,9 @@ LuaDefine(unittests_world2pairtab, "", "some unit tests") { } LuaDefine(unittests_world3diffluatab, "", "some unit tests") { - UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); - UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); - UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC)); + UniqueWorld m(new World(WORLD_TYPE_MASTER)); + UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE)); + UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE)); StreamBuffer sb; // Initialize all three models so that a tangible exists. @@ -413,9 +413,9 @@ LuaDefine(unittests_world3diffluatab, "", "some unit tests") { } LuaDefine(unittests_world4difftanclass, "", "some unit tests") { - UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); - UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); - UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC)); + UniqueWorld m(new World(WORLD_TYPE_MASTER)); + UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE)); + UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE)); StreamBuffer sb; // Initialize all three models so that a tangible exists. diff --git a/luprex/cpp/core/world.hpp b/luprex/cpp/core/world.hpp index b506d91d..6c416105 100644 --- a/luprex/cpp/core/world.hpp +++ b/luprex/cpp/core/world.hpp @@ -104,7 +104,7 @@ public: // The constructor also calls 'lua_open' to create a new // lua interpreter for this world model. // - World(util::WorldType wt); + World(WorldType wt); // Destructor. // @@ -236,8 +236,8 @@ public: // Check if the world is authoritative. // - bool is_authoritative() const { return (world_type_ == util::WORLD_TYPE_MASTER) || (world_type_ == util::WORLD_TYPE_STANDALONE); } - + bool is_authoritative() const { return LuaStack::is_authoritative(world_type_); } + // Get a table showing all outstanding HTTP requests. // const HttpClientRequestMap &http_requests() const { return http_requests_; } @@ -249,6 +249,9 @@ public: // Snapshot and rollback. // + // These are used by the client to convert the synchronous model + // to an asynchronous model and back. + // void snapshot(); void rollback(); bool snapshot_empty() { return snapshot_.empty(); } @@ -494,7 +497,7 @@ public: private: // Type of model - util::WorldType world_type_; + WorldType world_type_; // A lua intepreter with snapshot function. //