Numbering and pairing of lua tables
This commit is contained in:
@@ -52,6 +52,8 @@ void LuaSnap::serialize(StreamBuffer *sb) {
|
|||||||
LS.rawset(regcopy, "unpersist", LuaNil);
|
LS.rawset(regcopy, "unpersist", LuaNil);
|
||||||
LS.rawset(regcopy, "world", LuaNil);
|
LS.rawset(regcopy, "world", LuaNil);
|
||||||
LS.rawset(regcopy, "gui", LuaNil);
|
LS.rawset(regcopy, "gui", LuaNil);
|
||||||
|
LS.rawset(regcopy, "tnmap", LuaNil);
|
||||||
|
LS.rawset(regcopy, "ntmap", LuaNil);
|
||||||
|
|
||||||
// Get the eris permanents table from the registry.
|
// Get the eris permanents table from the registry.
|
||||||
LS.rawget(permstable, LuaRegistry, "persist");
|
LS.rawget(permstable, LuaRegistry, "persist");
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ LuaFunctionReg::List LuaFunctionReg::all() {
|
|||||||
|
|
||||||
LuaFunctionReg *LuaFunctionReg::LuaFunctionRegistry;
|
LuaFunctionReg *LuaFunctionReg::LuaFunctionRegistry;
|
||||||
|
|
||||||
|
bool LuaStack::issortablekey(LuaSlot s) const {
|
||||||
|
int type = lua_type(L_, s);
|
||||||
|
return (type == LUA_TBOOLEAN) || (type == LUA_TNUMBER) || (type == LUA_TSTRING);
|
||||||
|
}
|
||||||
|
|
||||||
bool LuaStack::ckboolean(LuaSlot s) const {
|
bool LuaStack::ckboolean(LuaSlot s) const {
|
||||||
luaL_checktype(L_, s, LUA_TBOOLEAN);
|
luaL_checktype(L_, s, LUA_TBOOLEAN);
|
||||||
return lua_toboolean(L_, s) ? true:false;
|
return lua_toboolean(L_, s) ? true:false;
|
||||||
@@ -121,9 +126,15 @@ void LuaStack::setmetatable(LuaSlot tab, LuaSlot mt) const {
|
|||||||
lua_setmetatable(L_, tab);
|
lua_setmetatable(L_, tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaStack::getmetatable(LuaSlot mt, LuaSlot tab) const {
|
bool LuaStack::getmetatable(LuaSlot mt, LuaSlot tab) const {
|
||||||
lua_getmetatable(L_, tab);
|
if (lua_getmetatable(L_, tab)) {
|
||||||
lua_replace(L_, mt);
|
lua_replace(L_, mt);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
lua_pushnil(L_);
|
||||||
|
lua_replace(L_, mt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaStack::checknometa(LuaSlot index) const {
|
void LuaStack::checknometa(LuaSlot index) const {
|
||||||
@@ -231,6 +242,26 @@ void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const {
|
|||||||
LS.result();
|
LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LuaStack::movesortablekey(LuaSlot key, lua_State *L) {
|
||||||
|
int type = lua_type(L_, key);
|
||||||
|
switch (type) {
|
||||||
|
case LUA_TBOOLEAN:
|
||||||
|
lua_pushboolean(L, lua_toboolean(L_, key));
|
||||||
|
break;
|
||||||
|
case LUA_TNUMBER:
|
||||||
|
lua_pushnumber(L, lua_tonumber(L_, key));
|
||||||
|
break;
|
||||||
|
case LUA_TSTRING: {
|
||||||
|
size_t len;
|
||||||
|
const char *str = lua_tolstring(L_, key, &len);
|
||||||
|
lua_pushlstring(L, str, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
luaL_error(L, "movesortablekey: not a sortable key");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LuaStack::makesubtable(LuaSlot sub, LuaSlot tab, const char *name) const {
|
void LuaStack::makesubtable(LuaSlot sub, LuaSlot tab, const char *name) const {
|
||||||
rawget(sub, tab, name);
|
rawget(sub, tab, name);
|
||||||
if (istable(sub)) {
|
if (istable(sub)) {
|
||||||
|
|||||||
@@ -371,6 +371,7 @@ public:
|
|||||||
bool isboolean(LuaSlot s) const { return lua_type(L_, s) == LUA_TBOOLEAN; }
|
bool isboolean(LuaSlot s) const { return lua_type(L_, s) == LUA_TBOOLEAN; }
|
||||||
bool isnil(LuaSlot s) const { return lua_type(L_, s) == LUA_TNIL; }
|
bool isnil(LuaSlot s) const { return lua_type(L_, s) == LUA_TNIL; }
|
||||||
bool iscfunction(LuaSlot s) const { return lua_iscfunction(L_, s) != 0; }
|
bool iscfunction(LuaSlot s) const { return lua_iscfunction(L_, s) != 0; }
|
||||||
|
bool issortablekey(LuaSlot s) const;
|
||||||
|
|
||||||
void checktable(LuaSlot index) const { checktype(index, LUA_TTABLE); }
|
void checktable(LuaSlot index) const { checktype(index, LUA_TTABLE); }
|
||||||
void checkstring(LuaSlot index) const { checktype(index, LUA_TSTRING); }
|
void checkstring(LuaSlot index) const { checktype(index, LUA_TSTRING); }
|
||||||
@@ -389,7 +390,7 @@ public:
|
|||||||
|
|
||||||
void clearmetatable(LuaSlot tab) const;
|
void clearmetatable(LuaSlot tab) const;
|
||||||
void setmetatable(LuaSlot tab, LuaSlot mt) const;
|
void setmetatable(LuaSlot tab, LuaSlot mt) const;
|
||||||
void getmetatable(LuaSlot mt, LuaSlot tab) const;
|
bool getmetatable(LuaSlot mt, LuaSlot tab) const;
|
||||||
void checknometa(LuaSlot index) const;
|
void checknometa(LuaSlot index) const;
|
||||||
|
|
||||||
void newtable(LuaSlot target) const;
|
void newtable(LuaSlot target) const;
|
||||||
@@ -406,6 +407,8 @@ public:
|
|||||||
void makeclass(LuaSlot tab, LuaSlot name) const;
|
void makeclass(LuaSlot tab, LuaSlot name) const;
|
||||||
void getclass(LuaSlot tab, LuaSlot name) const;
|
void getclass(LuaSlot tab, LuaSlot name) const;
|
||||||
|
|
||||||
|
void movesortablekey(LuaSlot val, lua_State *L);
|
||||||
|
|
||||||
void makeclass(LuaSlot tab, const char *name) const {
|
void makeclass(LuaSlot tab, const char *name) const {
|
||||||
push_any_value(name);
|
push_any_value(name);
|
||||||
LuaSpecial classname(lua_gettop(L_));
|
LuaSpecial classname(lua_gettop(L_));
|
||||||
|
|||||||
@@ -16,6 +16,14 @@
|
|||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
|
|
||||||
|
IdVector id_vector_create(int64_t id1, int64_t id2, int64_t id3, int64_t id4) {
|
||||||
|
IdVector result;
|
||||||
|
if (id1 >= 0) result.push_back(id1);
|
||||||
|
if (id2 >= 0) result.push_back(id2);
|
||||||
|
if (id3 >= 0) result.push_back(id3);
|
||||||
|
if (id4 >= 0) result.push_back(id4);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string id_vector_debug_string(const IdVector &idv) {
|
std::string id_vector_debug_string(const IdVector &idv) {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ using StringVec = std::vector<std::string>;
|
|||||||
using HashValue = std::pair<uint64_t, uint64_t>;
|
using HashValue = std::pair<uint64_t, uint64_t>;
|
||||||
using IdVector = std::vector<int64_t>;
|
using IdVector = std::vector<int64_t>;
|
||||||
|
|
||||||
|
// ID vector quick create.
|
||||||
|
IdVector id_vector_create(int64_t id1=-1, int64_t id2=-1, int64_t id3=-1, int64_t id4=-1);
|
||||||
|
|
||||||
// ID vector debug string.
|
// ID vector debug string.
|
||||||
std::string id_vector_debug_string(const IdVector &idv);
|
std::string id_vector_debug_string(const IdVector &idv);
|
||||||
|
|
||||||
|
|||||||
@@ -190,7 +190,145 @@ std::string World::tangible_ids_debug_string() const {
|
|||||||
std::sort(idv.begin(), idv.end());
|
std::sort(idv.begin(), idv.end());
|
||||||
return util::id_vector_debug_string(idv);
|
return util::id_vector_debug_string(idv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string World::numbered_tables_debug_string() const {
|
||||||
|
lua_State *L = state();
|
||||||
|
LuaVar ntmap, tab, tid;
|
||||||
|
LuaStack LS(L, ntmap, tab, tid);
|
||||||
|
std::vector<std::string> result;
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
// Fetch the numbered tables map.
|
||||||
|
LS.rawget(ntmap, LuaRegistry, "ntmap");
|
||||||
|
|
||||||
|
// Iterate over the map. For each table, if it has
|
||||||
|
// a TID, output that. Otherwise, output "unknown".
|
||||||
|
for (int i = 1; i < 10000; i++) {
|
||||||
|
LS.rawgeti(tab, ntmap, i);
|
||||||
|
if (!LS.istable(tab)) break;
|
||||||
|
LS.rawget(tid, tab, "TID");
|
||||||
|
if (LS.isstring(tid)) {
|
||||||
|
result.push_back(LS.ckstring(tid));
|
||||||
|
} else {
|
||||||
|
result.push_back("unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LS.result();
|
||||||
|
std::sort(result.begin(), result.end());
|
||||||
|
for (const std::string &s : result) {
|
||||||
|
oss << s << ";";
|
||||||
|
}
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string World::paired_tables_debug_string(lua_State *master, const TablePairing *pairing) const {
|
||||||
|
lua_State *synch = state();
|
||||||
|
LuaVar mntmap, sntmap, mtab, stab, mtid, stid;
|
||||||
|
LuaStack MLS(master, mntmap, mtab, mtid);
|
||||||
|
LuaStack SLS(synch, sntmap, stab, stid);
|
||||||
|
std::vector<std::pair<std::string, std::string>> result;
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
// Fetch the numbered tables map.
|
||||||
|
MLS.rawget(mntmap, LuaRegistry, "ntmap");
|
||||||
|
SLS.rawget(sntmap, LuaRegistry, "ntmap");
|
||||||
|
|
||||||
|
for (int i = 1; i < pairing->mpair.size(); i++) {
|
||||||
|
if (pairing->mpair[i] == 0) continue;
|
||||||
|
MLS.rawget(mtab, mntmap, i);
|
||||||
|
SLS.rawget(stab, sntmap, pairing->mpair[i]);
|
||||||
|
std::string mname = "unknown";
|
||||||
|
std::string sname = "unknown";
|
||||||
|
if (MLS.istable(mtab)) {
|
||||||
|
MLS.rawget(mtid, mtab, "TID");
|
||||||
|
if (MLS.isstring(mtid)) {
|
||||||
|
mname = MLS.ckstring(mtid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SLS.istable(stab)) {
|
||||||
|
SLS.rawget(stid, stab, "TID");
|
||||||
|
if (SLS.isstring(stid)) {
|
||||||
|
sname = SLS.ckstring(stid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push_back(std::make_pair(mname, sname));
|
||||||
|
}
|
||||||
|
MLS.result();
|
||||||
|
SLS.result();
|
||||||
|
std::sort(result.begin(), result.end());
|
||||||
|
for (const auto &pair : result) {
|
||||||
|
oss << pair.first << "=" << pair.second << ";";
|
||||||
|
}
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::tangible_set_string(int64_t id, const std::string &path, const std::string &value) {
|
||||||
|
lua_State *L = state();
|
||||||
|
LuaVar tangibles, tab, subtab;
|
||||||
|
LuaStack LS(L, tangibles, tab, subtab);
|
||||||
|
|
||||||
|
// Fetch the lua side of the tangible.
|
||||||
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
||||||
|
LS.rawget(tab, tangibles, id);
|
||||||
|
assert(LS.istable(tab));
|
||||||
|
|
||||||
|
// Split the path parts into the table names and final part.
|
||||||
|
util::StringVec pathparts = util::split(path, '.');
|
||||||
|
assert(pathparts.size() >= 1);
|
||||||
|
std::string finalpart = pathparts.back();
|
||||||
|
pathparts.pop_back();
|
||||||
|
|
||||||
|
// Create subtables as necessary.
|
||||||
|
for (const std::string &subname : pathparts) {
|
||||||
|
LS.rawget(subtab, tab, subname);
|
||||||
|
if (LS.isnil(subtab)) {
|
||||||
|
LS.set(subtab, LuaNewTable);
|
||||||
|
LS.rawset(tab, subname, subtab);
|
||||||
|
}
|
||||||
|
assert(LS.istable(subtab));
|
||||||
|
LS.set(tab, subtab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the string value.
|
||||||
|
LS.rawset(tab, finalpart, value);
|
||||||
|
LS.result();
|
||||||
|
assert(stack_is_clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::tangible_copy_global(int64_t id, const std::string &path, const std::string &global) {
|
||||||
|
lua_State *L = state();
|
||||||
|
LuaVar tangibles, tab, subtab, globtab, value;
|
||||||
|
LuaStack LS(L, tangibles, tab, subtab, globtab, value);
|
||||||
|
|
||||||
|
// Fetch the lua side of the tangible.
|
||||||
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
||||||
|
LS.rawget(tab, tangibles, id);
|
||||||
|
assert(LS.istable(tab));
|
||||||
|
|
||||||
|
// Split the path parts into the table names and final part.
|
||||||
|
util::StringVec pathparts = util::split(path, '.');
|
||||||
|
assert(pathparts.size() >= 1);
|
||||||
|
std::string finalpart = pathparts.back();
|
||||||
|
pathparts.pop_back();
|
||||||
|
|
||||||
|
// Create subtables as necessary.
|
||||||
|
for (const std::string &subname : pathparts) {
|
||||||
|
LS.rawget(subtab, tab, subname);
|
||||||
|
if (LS.isnil(subtab)) {
|
||||||
|
LS.set(subtab, LuaNewTable);
|
||||||
|
LS.rawset(tab, subname, subtab);
|
||||||
|
}
|
||||||
|
assert(LS.istable(subtab));
|
||||||
|
LS.set(tab, subtab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the global value.
|
||||||
|
LS.getglobaltable(globtab);
|
||||||
|
LS.rawget(value, globtab, global);
|
||||||
|
LS.rawset(tab, finalpart, value);
|
||||||
|
LS.result();
|
||||||
|
//assert(stack_is_clear());
|
||||||
|
}
|
||||||
|
|
||||||
util::IdVector World::get_near_unsorted(int64_t player_id, float radius, bool exclude_nowhere) const {
|
util::IdVector World::get_near_unsorted(int64_t player_id, float radius, bool exclude_nowhere) const {
|
||||||
const Tangible *player = tangible_get(player_id);
|
const Tangible *player = tangible_get(player_id);
|
||||||
@@ -550,7 +688,7 @@ void World::rollback() {
|
|||||||
deserialize(&snapshot_);
|
deserialize(&snapshot_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::difference_transmit(int64_t actor_id, const World *master, StreamBuffer *sb) {
|
void World::difference_transmit(int64_t actor_id, World *master, StreamBuffer *sb) {
|
||||||
StreamBuffer tsb;
|
StreamBuffer tsb;
|
||||||
|
|
||||||
// Get the actor in both models. s_actor may be nil.
|
// Get the actor in both models. s_actor may be nil.
|
||||||
@@ -600,6 +738,19 @@ void World::difference_transmit(int64_t actor_id, const World *master, StreamBuf
|
|||||||
// Confirm the close tangibles with the client.
|
// Confirm the close tangibles with the client.
|
||||||
sb->write_hashvalue(closehash);
|
sb->write_hashvalue(closehash);
|
||||||
|
|
||||||
|
// Number tables in both the master and the synchronous model.
|
||||||
|
// Meanwhile, the client will number tables in the client-synchronous model.
|
||||||
|
master->number_lua_tables(closetans);
|
||||||
|
int s_ntables = number_lua_tables(closetans);
|
||||||
|
|
||||||
|
// Pair tables from the synchronous and master models.
|
||||||
|
TablePairing pairing;
|
||||||
|
pair_lua_tables(closetans, master->state(), &pairing);
|
||||||
|
int ncreate = pair_new_tables(&pairing);
|
||||||
|
sb->write_int32(s_ntables);
|
||||||
|
sb->write_int32(ncreate);
|
||||||
|
create_new_tables(ncreate);
|
||||||
|
|
||||||
assert(tsb.at_eof());
|
assert(tsb.at_eof());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,6 +761,10 @@ void World::apply_differences(StreamBuffer *sb) {
|
|||||||
util::HashValue closehash = util::hash_id_vector(closetans);
|
util::HashValue closehash = util::hash_id_vector(closetans);
|
||||||
util::HashValue m_closehash = sb->read_hashvalue();
|
util::HashValue m_closehash = sb->read_hashvalue();
|
||||||
assert(closehash == m_closehash);
|
assert(closehash == m_closehash);
|
||||||
|
int s_ntables = number_lua_tables(closetans);
|
||||||
|
assert(s_ntables == sb->read_int32());
|
||||||
|
int ncreate = sb->read_int32();
|
||||||
|
create_new_tables(ncreate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::diff_actor_essentials(const Tangible *m_actor, const Tangible *s_actor, StreamBuffer *sb) {
|
void World::diff_actor_essentials(const Tangible *m_actor, const Tangible *s_actor, StreamBuffer *sb) {
|
||||||
@@ -716,6 +871,200 @@ void World::patch_visible_animations(StreamBuffer *sb) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int World::number_lua_tables(const IdVector &basis) {
|
||||||
|
// This is conceptually recursive, but we're going to use an
|
||||||
|
// explicit stack (the lua stack).
|
||||||
|
lua_State *L = state();
|
||||||
|
LuaVar tnmap, ntmap, tangibles, tab, key, val, xid;
|
||||||
|
LuaStack LS(L, tnmap, ntmap, tangibles, tab, key, val, xid);
|
||||||
|
LS.set(tnmap, LuaNewTable);
|
||||||
|
LS.set(ntmap, LuaNewTable);
|
||||||
|
LS.rawset(LuaRegistry, "tnmap", tnmap);
|
||||||
|
LS.rawset(LuaRegistry, "ntmap", ntmap);
|
||||||
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
||||||
|
int nextid = 1;
|
||||||
|
int top = lua_gettop(L);
|
||||||
|
|
||||||
|
// Push all subtables onto the stack. Note that we may push
|
||||||
|
// the same table twice, that's OK.
|
||||||
|
for (int64_t id : basis) {
|
||||||
|
LS.rawget(tab, tangibles, id);
|
||||||
|
assert(LS.istable(tab));
|
||||||
|
// Maybe I should traverse the metatable?
|
||||||
|
// Traverse subtables.
|
||||||
|
LS.set(key, LuaNil);
|
||||||
|
while (LS.next(tab, key, val)) {
|
||||||
|
// if (LS.isstring(key)) {
|
||||||
|
// std::cerr << "Visiting tangible " << LS.ckstring(key) << std::endl;
|
||||||
|
// } else {
|
||||||
|
// std::cerr << "Visiting tangible xxxx" << std::endl;
|
||||||
|
// }
|
||||||
|
if (LS.istable(val) && LS.gettabletype(val)==LuaStack::TAB_GENERAL) {
|
||||||
|
lua_checkstack(L, 10);
|
||||||
|
lua_pushvalue(L, val.index());
|
||||||
|
// std::cerr << "Stack: " << lua_gettop(L) - top << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop tables from the stack one by one. If the table is not
|
||||||
|
// already numbered, number it and push subtables onto the stack.
|
||||||
|
while (lua_gettop(L) > top) {
|
||||||
|
//std::cerr << "Popping stack with " << lua_gettop(L) - top << std::endl;
|
||||||
|
lua_replace(L, tab.index());
|
||||||
|
LS.rawget(xid, tnmap, tab);
|
||||||
|
if (LS.isnil(xid)) {
|
||||||
|
int id = nextid++;
|
||||||
|
LS.rawset(tnmap, tab, id);
|
||||||
|
LS.rawset(ntmap, id, tab);
|
||||||
|
// Traverse the metatable.
|
||||||
|
LS.getmetatable(val, tab);
|
||||||
|
if (LS.istable(val) && LS.gettabletype(val)==LuaStack::TAB_GENERAL) {
|
||||||
|
lua_checkstack(L, 10);
|
||||||
|
lua_pushvalue(L, val.index());
|
||||||
|
}
|
||||||
|
// Traverse the subtables.
|
||||||
|
LS.set(key, LuaNil);
|
||||||
|
while (LS.next(tab, key, val)) {
|
||||||
|
// if (LS.isstring(key)) {
|
||||||
|
// std::cerr << "Visiting table " << LS.ckstring(key) << std::endl;
|
||||||
|
// } else {
|
||||||
|
// std::cerr << "Visiting table xxxx" << std::endl;
|
||||||
|
// }
|
||||||
|
if (LS.istable(val) && LS.gettabletype(val)==LuaStack::TAB_GENERAL) {
|
||||||
|
lua_checkstack(L, 10);
|
||||||
|
lua_pushvalue(L, val.index());
|
||||||
|
// std::cerr << "Stack: " << lua_gettop(L) - top << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LS.result();
|
||||||
|
assert(stack_is_clear());
|
||||||
|
return nextid - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::unnumber_lua_tables() {
|
||||||
|
// All we have to do is remove these tables from the registry.
|
||||||
|
LuaStack LS(state());
|
||||||
|
LS.rawset(LuaRegistry, "tnmap", LuaNil);
|
||||||
|
LS.rawset(LuaRegistry, "ntmap", LuaNil);
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::pair_lua_tables(const IdVector &basis, lua_State *master, TablePairing *pairing) {
|
||||||
|
lua_State *synch = state();
|
||||||
|
LuaVar stangibles, mtangibles, sntmap, mntmap, stnmap, mtnmap, stab, mtab, skey, mkey, sval, mval, sidx, midx;
|
||||||
|
LuaStack SLS(synch, stangibles, stab, skey, sval, sntmap, stnmap, sidx);
|
||||||
|
LuaStack MLS(master, mtangibles, mtab, mkey, mval, mntmap, mtnmap, midx);
|
||||||
|
|
||||||
|
SLS.rawget(stangibles, LuaRegistry, "tangibles");
|
||||||
|
MLS.rawget(mtangibles, LuaRegistry, "tangibles");
|
||||||
|
SLS.rawget(stnmap, LuaRegistry, "tnmap");
|
||||||
|
MLS.rawget(mtnmap, LuaRegistry, "tnmap");
|
||||||
|
SLS.rawget(sntmap, LuaRegistry, "ntmap");
|
||||||
|
MLS.rawget(mntmap, LuaRegistry, "ntmap");
|
||||||
|
assert(SLS.istable(stnmap));
|
||||||
|
assert(MLS.istable(mtnmap));
|
||||||
|
assert(SLS.istable(sntmap));
|
||||||
|
assert(MLS.istable(mntmap));
|
||||||
|
pairing->mpair.assign(MLS.rawlen(mntmap) + 1, 0);
|
||||||
|
pairing->spair.assign(SLS.rawlen(sntmap) + 1, 0);
|
||||||
|
int mtop = lua_gettop(master);
|
||||||
|
|
||||||
|
for (int64_t id : basis) {
|
||||||
|
MLS.rawget(mtab, mtangibles, id);
|
||||||
|
SLS.rawget(stab, stangibles, id);
|
||||||
|
assert(MLS.istable(mtab));
|
||||||
|
assert(SLS.istable(stab));
|
||||||
|
MLS.set(mkey, LuaNil);
|
||||||
|
while (MLS.next(mtab, mkey, mval)) {
|
||||||
|
if (!MLS.issortablekey(mkey)) continue;
|
||||||
|
if (!MLS.istable(mval)) continue;
|
||||||
|
MLS.movesortablekey(mkey, synch);
|
||||||
|
lua_replace(synch, skey.index());
|
||||||
|
SLS.rawget(sval, stab, skey);
|
||||||
|
if (!SLS.istable(sval)) continue;
|
||||||
|
lua_checkstack(master, 20);
|
||||||
|
lua_checkstack(synch, 20);
|
||||||
|
lua_pushvalue(master, mval.index());
|
||||||
|
lua_pushvalue(synch, sval.index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (lua_gettop(master) > mtop) {
|
||||||
|
lua_replace(master, mtab.index());
|
||||||
|
lua_replace(synch, stab.index());
|
||||||
|
MLS.rawget(midx, mtnmap, mtab);
|
||||||
|
if (!MLS.isnumber(midx)) continue;
|
||||||
|
SLS.rawget(sidx, stnmap, stab);
|
||||||
|
if (!SLS.isnumber(sidx)) continue;
|
||||||
|
int imidx = MLS.ckint(midx);
|
||||||
|
int isidx = SLS.ckint(sidx);
|
||||||
|
if (pairing->spair[isidx] != 0) continue;
|
||||||
|
if (pairing->mpair[imidx] != 0) continue;
|
||||||
|
pairing->spair[isidx] = imidx;
|
||||||
|
pairing->mpair[imidx] = isidx;
|
||||||
|
// Pair the metatables.
|
||||||
|
MLS.getmetatable(mval, mtab);
|
||||||
|
if (MLS.istable(mval)) {
|
||||||
|
SLS.getmetatable(sval, stab);
|
||||||
|
if (SLS.istable(sval)) {
|
||||||
|
lua_pushvalue(master, mval.index());
|
||||||
|
lua_pushvalue(synch, sval.index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Pair the subtables.
|
||||||
|
MLS.set(mkey, LuaNil);
|
||||||
|
while (MLS.next(mtab, mkey, mval)) {
|
||||||
|
if (!MLS.issortablekey(mkey)) continue;
|
||||||
|
if (!MLS.istable(mval)) continue;
|
||||||
|
MLS.movesortablekey(mkey, synch);
|
||||||
|
lua_replace(synch, skey.index());
|
||||||
|
SLS.rawget(sval, stab, skey);
|
||||||
|
if (!SLS.istable(sval)) continue;
|
||||||
|
lua_checkstack(master, 20);
|
||||||
|
lua_checkstack(synch, 20);
|
||||||
|
lua_pushvalue(master, mval.index());
|
||||||
|
lua_pushvalue(synch, sval.index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MLS.result();
|
||||||
|
SLS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
int World::pair_new_tables(TablePairing *pairing) {
|
||||||
|
int orig = pairing->spair.size();
|
||||||
|
for (int i = 1; i <= pairing->mpair.size(); i++) {
|
||||||
|
int id = pairing->mpair[i];
|
||||||
|
if (id == 0) {
|
||||||
|
pairing->mpair[i] = pairing->spair.size();
|
||||||
|
pairing->spair.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pairing->spair.size() - orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::create_new_tables(int n) {
|
||||||
|
LuaVar tnmap, ntmap, tab;
|
||||||
|
LuaStack LS(state(), tnmap, ntmap, tab);
|
||||||
|
LS.rawget(tnmap, LuaRegistry, "tnmap");
|
||||||
|
LS.rawget(ntmap, LuaRegistry, "ntmap");
|
||||||
|
assert(LS.istable(tnmap));
|
||||||
|
assert(LS.istable(ntmap));
|
||||||
|
int ntables = LS.rawlen(ntmap);
|
||||||
|
int nextid = ntables + 1;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int id = nextid++;
|
||||||
|
LS.newtable(tab);
|
||||||
|
LS.rawset(ntmap, id, tab);
|
||||||
|
LS.rawset(tnmap, tab, id);
|
||||||
|
}
|
||||||
|
LS.result();
|
||||||
|
assert(stack_is_clear());
|
||||||
|
}
|
||||||
|
|
||||||
LuaDefine(tangible_animstate, "c") {
|
LuaDefine(tangible_animstate, "c") {
|
||||||
LuaArg tanobj;
|
LuaArg tanobj;
|
||||||
LuaRet graphic, plane, x, y, z, facing;
|
LuaRet graphic, plane, x, y, z, facing;
|
||||||
@@ -895,6 +1244,7 @@ LuaDefine(world_settabletype, "f") {
|
|||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool worlds_identical(const std::unique_ptr<World> &w1, const std::unique_ptr<World> &w2) {
|
static bool worlds_identical(const std::unique_ptr<World> &w1, const std::unique_ptr<World> &w2) {
|
||||||
StreamBuffer sbw1, sbw2;
|
StreamBuffer sbw1, sbw2;
|
||||||
w1->serialize(&sbw1);
|
w1->serialize(&sbw1);
|
||||||
@@ -905,7 +1255,57 @@ static bool worlds_identical(const std::unique_ptr<World> &w1, const std::unique
|
|||||||
LuaDefine(unittests_world, "c") {
|
LuaDefine(unittests_world, "c") {
|
||||||
std::unique_ptr<World> m, ss, cs;
|
std::unique_ptr<World> m, ss, cs;
|
||||||
StreamBuffer sb;
|
StreamBuffer sb;
|
||||||
|
int m_ntables, s_ntables, ncreate;
|
||||||
|
|
||||||
|
// Test the numbering of lua tables. We create some general
|
||||||
|
// tables using tangible_set_string. Then we install some
|
||||||
|
// specialty tables (not numberable) using tangible_copy_global.
|
||||||
|
// Finally, we number and dump the list of numbered tables.
|
||||||
|
m.reset(new World(util::WORLD_TYPE_MASTER));
|
||||||
|
m->tangible_make(0, 123, false);
|
||||||
|
m->tangible_set_string(123, "inventory.TID", "inventory");
|
||||||
|
m->tangible_set_string(123, "transactions.TID", "transactions");
|
||||||
|
m->tangible_set_string(123, "skills.TID", "skills");
|
||||||
|
m->tangible_set_string(123, "skills.leet.TID", "skills.leet");
|
||||||
|
m->tangible_set_string(123, "inventory.cplx.TID", "inventory.cplx");
|
||||||
|
m->tangible_copy_global(123, "math", "math");
|
||||||
|
m->tangible_copy_global(123, "gltab", "_G");
|
||||||
|
m_ntables = m->number_lua_tables(util::id_vector_create(123));
|
||||||
|
// LuaAssert(L, m_ntables == 7);
|
||||||
|
LuaAssertStrEq(L, m->numbered_tables_debug_string(),
|
||||||
|
"inventory;inventory.cplx;skills;skills.leet;transactions;");
|
||||||
|
|
||||||
|
// Now we're going to create a synchronous model that's similar to, but not
|
||||||
|
// exactly the same as that master model, and we're going to pair the tables.
|
||||||
|
// Only some of these tables should pair: inventory, skills, and skills.leet
|
||||||
|
ss.reset(new World(util::WORLD_TYPE_S_SYNC));
|
||||||
|
ss->tangible_make(0, 123, false);
|
||||||
|
ss->tangible_set_string(123, "inventory.TID", "inventory");
|
||||||
|
ss->tangible_set_string(123, "skills.TID", "skills");
|
||||||
|
ss->tangible_set_string(123, "skills.crap.TID", "skills.crap");
|
||||||
|
ss->tangible_set_string(123, "skills.leet.TID", "skills.leet");
|
||||||
|
ss->tangible_set_string(123, "math.TID", "math");
|
||||||
|
ss->tangible_set_string(123, "gltab.TID", "gltab");
|
||||||
|
s_ntables = ss->number_lua_tables(util::id_vector_create(123));
|
||||||
|
LuaAssert(L, s_ntables == 6);
|
||||||
|
LuaAssertStrEq(L, ss->numbered_tables_debug_string(),
|
||||||
|
"gltab;inventory;math;skills;skills.crap;skills.leet;");
|
||||||
|
World::TablePairing pairing;
|
||||||
|
ss->pair_lua_tables(util::id_vector_create(123), m->state(), &pairing);
|
||||||
|
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state(), &pairing),
|
||||||
|
"inventory=inventory;skills=skills;skills.leet=skills.leet;");
|
||||||
|
|
||||||
|
// Test the creation of new tables during difference transmission.
|
||||||
|
// The master world model above has two tables that couldn't be paired
|
||||||
|
// to the client: inventory.cplx, and transactions. These two tables
|
||||||
|
// should be paired to new, created tables.
|
||||||
|
ncreate = m->pair_new_tables(&pairing);
|
||||||
|
LuaAssert(L, ncreate == 2);
|
||||||
|
ss->create_new_tables(ncreate);
|
||||||
|
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state(), &pairing),
|
||||||
|
"inventory=inventory;inventory.cplx=unknown;skills=skills;skills.leet=skills.leet;transactions=unknown;");
|
||||||
|
|
||||||
|
// Create new clean world models.
|
||||||
m.reset(new World(util::WORLD_TYPE_MASTER));
|
m.reset(new World(util::WORLD_TYPE_MASTER));
|
||||||
ss.reset(new World(util::WORLD_TYPE_S_SYNC));
|
ss.reset(new World(util::WORLD_TYPE_S_SYNC));
|
||||||
cs.reset(new World(util::WORLD_TYPE_C_SYNC));
|
cs.reset(new World(util::WORLD_TYPE_C_SYNC));
|
||||||
|
|||||||
@@ -80,6 +80,10 @@ public:
|
|||||||
using IdVector = util::IdVector;
|
using IdVector = util::IdVector;
|
||||||
using TanVector = std::vector<const Tangible*>;
|
using TanVector = std::vector<const Tangible*>;
|
||||||
using Redirects = std::map<int64_t, int64_t>;
|
using Redirects = std::map<int64_t, int64_t>;
|
||||||
|
struct TablePairing {
|
||||||
|
std::vector<int> mpair;
|
||||||
|
std::vector<int> spair;
|
||||||
|
};
|
||||||
const float RadiusVisibility = 100.0;
|
const float RadiusVisibility = 100.0;
|
||||||
const float RadiusClose = 10.0;
|
const float RadiusClose = 10.0;
|
||||||
|
|
||||||
@@ -197,7 +201,7 @@ public:
|
|||||||
// so that they can be sent to the client. It also applies the diffs
|
// so that they can be sent to the client. It also applies the diffs
|
||||||
// to this model.
|
// to this model.
|
||||||
//
|
//
|
||||||
void difference_transmit(int64_t actor, const World *master, StreamBuffer *sb);
|
void difference_transmit(int64_t actor, World *master, StreamBuffer *sb);
|
||||||
|
|
||||||
// Apply differences.
|
// Apply differences.
|
||||||
//
|
//
|
||||||
@@ -227,7 +231,25 @@ public:
|
|||||||
// Get a list of all existing tangibles as a comma-separated string.
|
// Get a list of all existing tangibles as a comma-separated string.
|
||||||
//
|
//
|
||||||
std::string tangible_ids_debug_string() const;
|
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 TablePairing *pairing) 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);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Run any threads which according to the scheduler queue are ready.
|
// Run any threads which according to the scheduler queue are ready.
|
||||||
//
|
//
|
||||||
@@ -261,6 +283,34 @@ private:
|
|||||||
static void diff_visible_animations(const TanVector &mvis, const TanVector &svis, StreamBuffer *sb);
|
static void diff_visible_animations(const TanVector &mvis, const TanVector &svis, StreamBuffer *sb);
|
||||||
void patch_visible_animations(StreamBuffer *sb);
|
void patch_visible_animations(StreamBuffer *sb);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Pass 3 of difference transmission: numbering of tables.
|
||||||
|
//
|
||||||
|
// Table-to-number mapping is stored in registry.tnmap
|
||||||
|
// Number-to-table mapping is stored in registry.ntmap
|
||||||
|
// Returns the total number of tables numbered.
|
||||||
|
//
|
||||||
|
int number_lua_tables(const IdVector &basis);
|
||||||
|
|
||||||
|
// Deletes registry.tnmap and registry.ntmap
|
||||||
|
//
|
||||||
|
void unnumber_lua_tables();
|
||||||
|
|
||||||
|
// Associates numbered tables in the master model with numbered tables in the synch model.
|
||||||
|
//
|
||||||
|
void pair_lua_tables(const IdVector &basis, lua_State *master, TablePairing *pair);
|
||||||
|
|
||||||
|
// Pairs any not-yet-paired master table to a virtually-created table in the synch model.
|
||||||
|
//
|
||||||
|
// Returns the number of new tables that need to be created in the synch model.
|
||||||
|
//
|
||||||
|
int pair_new_tables(TablePairing *pair);
|
||||||
|
|
||||||
|
// This is followup for pair_new_tables: actually create the new tables that
|
||||||
|
// were virtually created in the pair_new_tables step.
|
||||||
|
//
|
||||||
|
void create_new_tables(int n);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Type of model
|
// Type of model
|
||||||
util::WorldType world_type_;
|
util::WorldType world_type_;
|
||||||
|
|||||||
Reference in New Issue
Block a user