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:
@@ -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
|
||||
|
||||
@@ -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]; }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); }
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,7 +236,7 @@ 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.
|
||||
//
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user