From 7581ac72788628685ded87d023eabc076bcd5683 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Fri, 13 Aug 2021 17:02:35 -0400 Subject: [PATCH] Numbering and pairing of lua tables --- luprex/core/cpp/luasnap.cpp | 2 + luprex/core/cpp/luastack.cpp | 37 +++- luprex/core/cpp/luastack.hpp | 5 +- luprex/core/cpp/util.cpp | 8 + luprex/core/cpp/util.hpp | 3 + luprex/core/cpp/world.cpp | 402 ++++++++++++++++++++++++++++++++++- luprex/core/cpp/world.hpp | 54 ++++- 7 files changed, 504 insertions(+), 7 deletions(-) diff --git a/luprex/core/cpp/luasnap.cpp b/luprex/core/cpp/luasnap.cpp index a04b99f0..719a7bd1 100644 --- a/luprex/core/cpp/luasnap.cpp +++ b/luprex/core/cpp/luasnap.cpp @@ -52,6 +52,8 @@ void LuaSnap::serialize(StreamBuffer *sb) { LS.rawset(regcopy, "unpersist", LuaNil); LS.rawset(regcopy, "world", LuaNil); LS.rawset(regcopy, "gui", LuaNil); + LS.rawset(regcopy, "tnmap", LuaNil); + LS.rawset(regcopy, "ntmap", LuaNil); // Get the eris permanents table from the registry. LS.rawget(permstable, LuaRegistry, "persist"); diff --git a/luprex/core/cpp/luastack.cpp b/luprex/core/cpp/luastack.cpp index 8c0fc5cc..ebc6db93 100644 --- a/luprex/core/cpp/luastack.cpp +++ b/luprex/core/cpp/luastack.cpp @@ -23,6 +23,11 @@ LuaFunctionReg::List LuaFunctionReg::all() { 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 { luaL_checktype(L_, s, LUA_TBOOLEAN); return lua_toboolean(L_, s) ? true:false; @@ -121,9 +126,15 @@ void LuaStack::setmetatable(LuaSlot tab, LuaSlot mt) const { lua_setmetatable(L_, tab); } -void LuaStack::getmetatable(LuaSlot mt, LuaSlot tab) const { - lua_getmetatable(L_, tab); - lua_replace(L_, mt); +bool LuaStack::getmetatable(LuaSlot mt, LuaSlot tab) const { + if (lua_getmetatable(L_, tab)) { + lua_replace(L_, mt); + return true; + } else { + lua_pushnil(L_); + lua_replace(L_, mt); + return false; + } } void LuaStack::checknometa(LuaSlot index) const { @@ -231,6 +242,26 @@ void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const { 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 { rawget(sub, tab, name); if (istable(sub)) { diff --git a/luprex/core/cpp/luastack.hpp b/luprex/core/cpp/luastack.hpp index 34d583d5..8668416b 100644 --- a/luprex/core/cpp/luastack.hpp +++ b/luprex/core/cpp/luastack.hpp @@ -371,6 +371,7 @@ public: 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 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 checkstring(LuaSlot index) const { checktype(index, LUA_TSTRING); } @@ -389,7 +390,7 @@ public: void clearmetatable(LuaSlot tab) 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 newtable(LuaSlot target) const; @@ -406,6 +407,8 @@ public: void makeclass(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 { push_any_value(name); LuaSpecial classname(lua_gettop(L_)); diff --git a/luprex/core/cpp/util.cpp b/luprex/core/cpp/util.cpp index 0ddec568..6bf872c5 100644 --- a/luprex/core/cpp/util.cpp +++ b/luprex/core/cpp/util.cpp @@ -16,6 +16,14 @@ 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::ostringstream oss; diff --git a/luprex/core/cpp/util.hpp b/luprex/core/cpp/util.hpp index 6ecd04dc..83b4ee80 100644 --- a/luprex/core/cpp/util.hpp +++ b/luprex/core/cpp/util.hpp @@ -24,6 +24,9 @@ using StringVec = std::vector; using HashValue = std::pair; using IdVector = std::vector; +// 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. std::string id_vector_debug_string(const IdVector &idv); diff --git a/luprex/core/cpp/world.cpp b/luprex/core/cpp/world.cpp index 02b9ae0a..35e57f14 100644 --- a/luprex/core/cpp/world.cpp +++ b/luprex/core/cpp/world.cpp @@ -190,7 +190,145 @@ std::string World::tangible_ids_debug_string() const { std::sort(idv.begin(), idv.end()); 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 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> 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 { const Tangible *player = tangible_get(player_id); @@ -550,7 +688,7 @@ void World::rollback() { 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; // 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. 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()); } @@ -610,6 +761,10 @@ void World::apply_differences(StreamBuffer *sb) { util::HashValue closehash = util::hash_id_vector(closetans); util::HashValue m_closehash = sb->read_hashvalue(); 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) { @@ -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") { LuaArg tanobj; LuaRet graphic, plane, x, y, z, facing; @@ -895,6 +1244,7 @@ LuaDefine(world_settabletype, "f") { return LS.result(); } + static bool worlds_identical(const std::unique_ptr &w1, const std::unique_ptr &w2) { StreamBuffer sbw1, sbw2; w1->serialize(&sbw1); @@ -905,7 +1255,57 @@ static bool worlds_identical(const std::unique_ptr &w1, const std::unique LuaDefine(unittests_world, "c") { std::unique_ptr m, ss, cs; 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)); ss.reset(new World(util::WORLD_TYPE_S_SYNC)); cs.reset(new World(util::WORLD_TYPE_C_SYNC)); diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index 48825004..ab8a4216 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -80,6 +80,10 @@ public: using IdVector = util::IdVector; using TanVector = std::vector; using Redirects = std::map; + struct TablePairing { + std::vector mpair; + std::vector spair; + }; const float RadiusVisibility = 100.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 // 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. // @@ -227,7 +231,25 @@ public: // Get a list of all existing tangibles as a comma-separated string. // 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: // 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); 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: // Type of model util::WorldType world_type_;