Refactor code to make it easier to do 'nopredict' inside of any function without having a dependency on world model.

This commit is contained in:
2023-03-01 16:07:13 -05:00
parent aa77480fb5
commit 9ce34d950b
15 changed files with 86 additions and 49 deletions

View File

@@ -323,8 +323,8 @@ bool AnimStep::from_string(const eng::string &config) {
return true; return true;
} }
AnimQueue::AnimQueue(util::WorldType wt) { AnimQueue::AnimQueue(WorldType wt) {
version_autoinc_ = (wt == util::WORLD_TYPE_MASTER); version_autoinc_ = (wt == WORLD_TYPE_MASTER);
size_limit_ = 10; // Default size limit. size_limit_ = 10; // Default size limit.
steps_.emplace_back(); steps_.emplace_back();
steps_.front().keep_state_only(); steps_.front().keep_state_only();
@@ -576,8 +576,8 @@ static bool diff_works(const AnimQueue &master, AnimQueue &sync) {
LuaDefine(unittests_animqueue, "", "some unit tests") { LuaDefine(unittests_animqueue, "", "some unit tests") {
// Useful objects. // Useful objects.
AnimStep stp; AnimStep stp;
AnimQueue aq(util::WORLD_TYPE_MASTER); AnimQueue aq(WORLD_TYPE_MASTER);
AnimQueue aqds(util::WORLD_TYPE_S_SYNC); AnimQueue aqds(WORLD_TYPE_PREDICTIVE);
StreamBuffer sb; StreamBuffer sb;
// Debug string of a newly initialized queue // Debug string of a newly initialized queue

View File

@@ -169,7 +169,7 @@ private:
class AnimQueue : public eng::nevernew { class AnimQueue : public eng::nevernew {
public: public:
// World type determines whether versions increment or autozero // World type determines whether versions increment or autozero
AnimQueue(util::WorldType wt); AnimQueue(WorldType wt);
// Simple getters. // Simple getters.
const AnimStep &nth(int n) const { return steps_[n]; } const AnimStep &nth(int n) const { return steps_[n]; }

View File

@@ -114,7 +114,7 @@ private:
void event_init(int argc, char *argv[]) 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_->update_source(get_lua_source());
world_->run_unittests(); world_->run_unittests();
stop_driver(); stop_driver();

View File

@@ -7,6 +7,8 @@ LuaDefine(global_once, "name", "for a given string, returns true exactly once")
LuaVar oncedb, val; LuaVar oncedb, val;
LuaStack LS(L, name, flag, oncedb, val); LuaStack LS(L, name, flag, oncedb, val);
LS.guard_nopredict("global.once");
// Get a pointer to the oncedb. // Get a pointer to the oncedb.
LS.rawget(oncedb, LuaRegistry, "oncedb"); LS.rawget(oncedb, LuaRegistry, "oncedb");
if (!LS.istable(oncedb)) { if (!LS.istable(oncedb)) {
@@ -25,11 +27,14 @@ LuaDefine(global_once, "name", "for a given string, returns true exactly once")
return LS.result(); return LS.result();
} }
LuaDefine(global_clearonce, "name", "reset the specified once-flag") { LuaDefine(global_clearonce, "name", "reset the specified once-flag") {
LuaArg name; LuaArg name;
LuaVar oncedb; LuaVar oncedb;
LuaStack LS(L, name, oncedb); LuaStack LS(L, name, oncedb);
LS.guard_nopredict("global.clearonce");
// Get a pointer to the oncedb. // Get a pointer to the oncedb.
LS.rawget(oncedb, LuaRegistry, "oncedb"); LS.rawget(oncedb, LuaRegistry, "oncedb");
if (!LS.istable(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. // Get a pointer to the globaldb.
LS.rawget(globaldb, LuaRegistry, "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. // Get the globaltab from the globaldb, sanity check it.
LS.rawget(globaltab, globaldb, globalname); LS.rawget(globaltab, globaldb, globalname);
if (LS.istable(globaltab)) { if (LS.istable(globaltab)) {

View File

@@ -24,7 +24,7 @@ public:
public: public:
void set_initial_state() { void set_initial_state() {
// Create the world model. // 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 // 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 // us the first difference transmission. We do this only to establish

View File

@@ -33,7 +33,7 @@ public:
public: public:
virtual void event_init(int argc, char *argv[]) { virtual void event_init(int argc, char *argv[]) {
// Create the master world model. // 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. // Update the source code of the master model.
master_->update_source(get_lua_source()); master_->update_source(get_lua_source());
@@ -189,7 +189,7 @@ public:
Client *client = new Client; Client *client = new Client;
client->actor_id_ = master_->create_login_actor(); client->actor_id_ = master_->create_login_actor();
client->channel_ = std::move(chan); 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(); client->sync_->create_login_actor();
clients_.emplace_back(client); clients_.emplace_back(client);
stdostream() << "New client: actor id=" << client->actor_id_ << std::endl; stdostream() << "New client: actor id=" << client->actor_id_ << std::endl;

View File

@@ -85,7 +85,8 @@ void LuaSnap::deserialize(StreamBuffer *sb) {
void *ud = sb->lua_reader_ud(len); void *ud = sb->lua_reader_ud(len);
// Call eris with the permanents table and passing the snapshot as a lua_Reader. // 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); eris_undump(state_, sb->lua_reader, ud);
assert(lua_gettop(state_) == 2); assert(lua_gettop(state_) == 2);

View File

@@ -466,6 +466,24 @@ void LuaStack::setvisited(LuaSlot tab, bool visited) const {
lua_modflagbits(L_, tab.index(), 0x0010, visited ? 0x0010 : 0); 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) { LuaKeywordParser::LuaKeywordParser(lua_State *L, int slot) {
L_ = L; L_ = L;
slot_ = slot; slot_ = slot;

View File

@@ -226,6 +226,13 @@ int LuaTypeTagValue(lua_State *L) { return 0; }
#define LUA_TT_GLOBALDB 22 #define LUA_TT_GLOBALDB 22
#define LUA_TT_CLASS 23 #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 // We use lightuserdata to store 'tokens': short
// strings of 8 characters or less. These tokens // strings of 8 characters or less. These tokens
// are useful as unique markers. The 8 characters // are useful as unique markers. The 8 characters
@@ -517,6 +524,17 @@ public:
bool getvisited(LuaSlot tab) const; bool getvisited(LuaSlot tab) const;
void setvisited(LuaSlot tab, bool visited) 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. // 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); } static bool int64_storable(int64_t v) { return (v <= MAXINT) && (v >= -MAXINT); }
}; };

View File

@@ -98,7 +98,7 @@ private:
void event_init(int argc, char *argv[]) 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_->update_source(get_lua_source());
world_->run_unittests(); world_->run_unittests();
actor_id_ = world_->create_login_actor(); actor_id_ = world_->create_login_actor();

View File

@@ -198,13 +198,6 @@ bool valid_number(string_view v, bool plus, bool minus, bool dec, bool exp);
namespace util { namespace util {
enum WorldType {
WORLD_TYPE_STANDALONE,
WORLD_TYPE_C_SYNC,
WORLD_TYPE_S_SYNC,
WORLD_TYPE_MASTER,
};
enum MessageType { enum MessageType {
MSG_NULL, MSG_NULL,
MSG_DIFF, MSG_DIFF,

View File

@@ -29,12 +29,12 @@ World *World::fetch_global_pointer(lua_State *L) {
World::~World() { World::~World() {
} }
World::World(util::WorldType wt) { World::World(WorldType wt) {
// Master world model by default. // Master world model by default.
world_type_ = wt; world_type_ = wt;
// Initialize the ID allocator in master mode. // 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(); id_global_pool_.init_master();
} else { } else {
id_global_pool_.init_synch(); id_global_pool_.init_synch();
@@ -63,11 +63,12 @@ World::World(util::WorldType wt) {
// Create the tangibles table in the registry. // Create the tangibles table in the registry.
LS.rawset(LuaRegistry, "tangibles", LuaNewTable); 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. // 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, "globaldb", LuaNewTable); LS.rawset(LuaRegistry, "oncedb", LuaNewTable);
LS.rawset(LuaRegistry, "oncedb", LuaNewTable);
}
// Initialize the SourceDB. At this stage, the sourcedb is // Initialize the SourceDB. At this stage, the sourcedb is
// empty, so it's just populating the lua builtins. // 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) { void World::guard_blockable(lua_State *L, const char *fn) {
if (lthread_thread_id_ == 0) { 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); luaL_error(L, "cannot %s in a probe", fn);
assert(false); assert(false);
} }
if (!is_authoritative()) { 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); lua_yield(L, 0);
luaL_error(L, "unexplained nopredict failure in %s", fn); luaL_error(L, "unexplained nopredict failure in %s", fn);
assert(false); 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) { 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) { if (lthread_thread_id_ == 0) {
return; return;
} }
@@ -883,7 +886,10 @@ void World::run_scheduled_threads() {
LS.rawset(thinfo, "isnew", false); LS.rawset(thinfo, "isnew", false);
LS.rawset(thinfo, "useppool", false); LS.rawset(thinfo, "useppool", false);
} else { } 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); LS.rawset(threads, sched.thread_id(), LuaNil);
} }
} else { } else {

View File

@@ -136,8 +136,6 @@ void World::pair_lua_tables(const IdVector &basis, lua_State *master) {
// If the master table is already paired, skip. // If the master table is already paired, skip.
MLS.rawget(midx, mtnmap, mtab); MLS.rawget(midx, mtnmap, mtab);
if (MLS.isnumber(midx)) continue; 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. // If the synch table doesn't have a number, skip.
SLS.rawget(sidx, stnmap, stab); SLS.rawget(sidx, stnmap, stab);
if (!SLS.isnumber(sidx)) continue; if (!SLS.isnumber(sidx)) continue;

View File

@@ -241,9 +241,9 @@ static bool worlds_identical(const UniqueWorld &w1, const UniqueWorld &w2) {
} }
LuaDefine(unittests_world1animdiff, "", "some unit tests") { LuaDefine(unittests_world1animdiff, "", "some unit tests") {
UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC)); UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb; StreamBuffer sb;
util::IdVector ids = util::id_vector_create(123, 345); 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") { LuaDefine(unittests_world2pairtab, "", "some unit tests") {
UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb; StreamBuffer sb;
int ncreate; int ncreate;
@@ -359,9 +359,9 @@ LuaDefine(unittests_world2pairtab, "", "some unit tests") {
} }
LuaDefine(unittests_world3diffluatab, "", "some unit tests") { LuaDefine(unittests_world3diffluatab, "", "some unit tests") {
UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC)); UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb; StreamBuffer sb;
// Initialize all three models so that a tangible exists. // 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") { LuaDefine(unittests_world4difftanclass, "", "some unit tests") {
UniqueWorld m(new World(util::WORLD_TYPE_MASTER)); UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(util::WORLD_TYPE_S_SYNC)); UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(util::WORLD_TYPE_C_SYNC)); UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb; StreamBuffer sb;
// Initialize all three models so that a tangible exists. // Initialize all three models so that a tangible exists.

View File

@@ -104,7 +104,7 @@ public:
// The constructor also calls 'lua_open' to create a new // The constructor also calls 'lua_open' to create a new
// lua interpreter for this world model. // lua interpreter for this world model.
// //
World(util::WorldType wt); World(WorldType wt);
// Destructor. // Destructor.
// //
@@ -236,8 +236,8 @@ public:
// Check if the world is authoritative. // 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. // Get a table showing all outstanding HTTP requests.
// //
const HttpClientRequestMap &http_requests() const { return http_requests_; } const HttpClientRequestMap &http_requests() const { return http_requests_; }
@@ -249,6 +249,9 @@ public:
// Snapshot and rollback. // Snapshot and rollback.
// //
// These are used by the client to convert the synchronous model
// to an asynchronous model and back.
//
void snapshot(); void snapshot();
void rollback(); void rollback();
bool snapshot_empty() { return snapshot_.empty(); } bool snapshot_empty() { return snapshot_.empty(); }
@@ -494,7 +497,7 @@ public:
private: private:
// Type of model // Type of model
util::WorldType world_type_; WorldType world_type_;
// A lua intepreter with snapshot function. // A lua intepreter with snapshot function.
// //