Working on diff xmission
This commit is contained in:
@@ -13,6 +13,7 @@ CPP_FILES=\
|
|||||||
cpp/globaldb.cpp\
|
cpp/globaldb.cpp\
|
||||||
cpp/sched.cpp\
|
cpp/sched.cpp\
|
||||||
cpp/table.cpp\
|
cpp/table.cpp\
|
||||||
|
cpp/tablecmp.cpp\
|
||||||
cpp/gui.cpp\
|
cpp/gui.cpp\
|
||||||
cpp/luasnap.cpp\
|
cpp/luasnap.cpp\
|
||||||
cpp/animqueue.cpp\
|
cpp/animqueue.cpp\
|
||||||
|
|||||||
@@ -45,6 +45,6 @@ LuaDefine(globaldb_global, "f") {
|
|||||||
LS.newtable(globaltab);
|
LS.newtable(globaltab);
|
||||||
LS.rawset(globaldb, globalname, globaltab);
|
LS.rawset(globaldb, globalname, globaltab);
|
||||||
LS.rawset(globaltab, "__global", globalname);
|
LS.rawset(globaltab, "__global", globalname);
|
||||||
LS.settabletype(globaltab, LuaStack::TAB_GLOBALDB);
|
LS.settabletype(globaltab, LUA_TT_GLOBALDB);
|
||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "luastack.hpp"
|
#include "luastack.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
LuaSpecial LuaRegistry(LUA_REGISTRYINDEX);
|
LuaSpecial LuaRegistry(LUA_REGISTRYINDEX);
|
||||||
LuaNilMarker LuaNil;
|
LuaNilMarker LuaNil;
|
||||||
@@ -234,7 +235,7 @@ void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Repair the special fields.
|
// Repair the special fields.
|
||||||
LS.settabletype(classtab, TAB_CLASS);
|
LS.settabletype(classtab, LUA_TT_CLASS);
|
||||||
LS.rawset(classtab, "__index", classtab);
|
LS.rawset(classtab, "__index", classtab);
|
||||||
LS.rawset(classtab, "__class", classname);
|
LS.rawset(classtab, "__class", classname);
|
||||||
|
|
||||||
@@ -242,23 +243,48 @@ void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const {
|
|||||||
LS.result();
|
LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaStack::movesortablekey(LuaSlot key, lua_State *L) {
|
void LuaStack::makeclass(LuaSlot tab, const char *name) {
|
||||||
|
push_any_value(name);
|
||||||
|
LuaSpecial classname(lua_gettop(L_));
|
||||||
|
makeclass(tab, classname);
|
||||||
|
lua_pop(L_, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LuaStack::classname(LuaSlot tab) {
|
||||||
|
std::string result;
|
||||||
|
if (istable(tab)) {
|
||||||
|
lua_pushstring(L_, "__class");
|
||||||
|
lua_rawget(L_, tab);
|
||||||
|
if (lua_type(L_, -1) == LUA_TSTRING) {
|
||||||
|
size_t len;
|
||||||
|
const char *s = lua_tolstring(L_, -1, &len);
|
||||||
|
result = std::string(s, len);
|
||||||
|
}
|
||||||
|
lua_pop(L_, 1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LuaStack::movesortablekey(LuaSlot key, LuaStack &otherstack, LuaSlot otherslot) {
|
||||||
int type = lua_type(L_, key);
|
int type = lua_type(L_, key);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case LUA_TBOOLEAN:
|
case LUA_TBOOLEAN:
|
||||||
lua_pushboolean(L, lua_toboolean(L_, key));
|
lua_pushboolean(otherstack.L_, lua_toboolean(L_, key));
|
||||||
|
lua_replace(otherstack.L_, otherslot);
|
||||||
break;
|
break;
|
||||||
case LUA_TNUMBER:
|
case LUA_TNUMBER:
|
||||||
lua_pushnumber(L, lua_tonumber(L_, key));
|
lua_pushnumber(otherstack.L_, lua_tonumber(L_, key));
|
||||||
|
lua_replace(otherstack.L_, otherslot);
|
||||||
break;
|
break;
|
||||||
case LUA_TSTRING: {
|
case LUA_TSTRING: {
|
||||||
size_t len;
|
size_t len;
|
||||||
const char *str = lua_tolstring(L_, key, &len);
|
const char *str = lua_tolstring(L_, key, &len);
|
||||||
lua_pushlstring(L, str, len);
|
lua_pushlstring(otherstack.L_, str, len);
|
||||||
|
lua_replace(otherstack.L_, otherslot);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
luaL_error(L, "movesortablekey: not a sortable key");
|
luaL_error(L_, "movesortablekey: not a sortable key");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,20 +323,29 @@ void LuaStack::check_nret(int xnret, int otop, int nret) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaStack::TableType LuaStack::gettabletype(LuaSlot tab) {
|
int LuaStack::gettabletype(LuaSlot tab) const {
|
||||||
uint16_t bits = lua_getflagbits(L_, tab.index());
|
uint16_t bits = lua_getflagbits(L_, tab.index());
|
||||||
return TableType(bits & 0x000F);
|
return LUA_TT_GENERAL + (bits & 0x000F);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaStack::settabletype(LuaSlot tab, TableType t) {
|
void LuaStack::settabletype(LuaSlot tab, int t) const {
|
||||||
lua_modflagbits(L_, tab.index(), 0x000F, t);
|
assert((t >= LUA_TT_GENERAL) && (t <= LUA_TT_CLASS));
|
||||||
|
int offset = (t - LUA_TT_GENERAL);
|
||||||
|
lua_modflagbits(L_, tab.index(), 0x000F, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LuaStack::getvisited(LuaSlot tab) {
|
int LuaStack::xtype(LuaSlot slot) const {
|
||||||
|
int t = lua_type(L_, slot);
|
||||||
|
if (t != LUA_TTABLE) return t;
|
||||||
|
uint16_t bits = lua_getflagbits(L_, slot);
|
||||||
|
return LUA_TT_GENERAL + (bits & 0x000F);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LuaStack::getvisited(LuaSlot tab) const {
|
||||||
uint16_t bits = lua_getflagbits(L_, tab.index());
|
uint16_t bits = lua_getflagbits(L_, tab.index());
|
||||||
return (bits & 0x0010);
|
return (bits & 0x0010);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LuaStack::setvisited(LuaSlot tab, bool visited) {
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,6 +215,18 @@ using LuaTypeTag = lua_CFunction;
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
int LuaTypeTagValue(lua_State *L) { return 0; }
|
int LuaTypeTagValue(lua_State *L) { return 0; }
|
||||||
|
|
||||||
|
// Lua table types. These deliberately do not overlap
|
||||||
|
// with lua type values.
|
||||||
|
//
|
||||||
|
#define LUA_TT_GENERAL 16
|
||||||
|
#define LUA_TT_REGISTRY 17
|
||||||
|
#define LUA_TT_GLOBALENV 18
|
||||||
|
#define LUA_TT_TANGIBLE 19
|
||||||
|
#define LUA_TT_TANGIBLEMETA 20
|
||||||
|
#define LUA_TT_DEADTANGIBLE 21
|
||||||
|
#define LUA_TT_GLOBALDB 22
|
||||||
|
#define LUA_TT_CLASS 23
|
||||||
|
|
||||||
class LuaStack {
|
class LuaStack {
|
||||||
private:
|
private:
|
||||||
int narg_;
|
int narg_;
|
||||||
@@ -360,6 +372,8 @@ public:
|
|||||||
int result();
|
int result();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
lua_State *state() const { return L_; }
|
||||||
|
|
||||||
int type(LuaSlot s) const { return lua_type(L_, s); }
|
int type(LuaSlot s) const { return lua_type(L_, s); }
|
||||||
void checktype(LuaSlot s, int type) const { luaL_checktype(L_, s, type); }
|
void checktype(LuaSlot s, int type) const { luaL_checktype(L_, s, type); }
|
||||||
|
|
||||||
@@ -404,17 +418,13 @@ public:
|
|||||||
|
|
||||||
int next(LuaSlot tab, LuaSlot key, LuaSlot value) const;
|
int next(LuaSlot tab, LuaSlot key, LuaSlot value) const;
|
||||||
|
|
||||||
void makeclass(LuaSlot tab, LuaSlot name) const;
|
|
||||||
void getclass(LuaSlot tab, LuaSlot name) const;
|
void getclass(LuaSlot tab, LuaSlot name) const;
|
||||||
|
void makeclass(LuaSlot tab, LuaSlot name) const;
|
||||||
|
void makeclass(LuaSlot tab, const char *name);
|
||||||
|
std::string classname(LuaSlot tab);
|
||||||
|
|
||||||
void movesortablekey(LuaSlot val, lua_State *L);
|
void movesortablekey(LuaSlot val, LuaStack &other, LuaSlot otherslot);
|
||||||
|
|
||||||
void makeclass(LuaSlot tab, const char *name) const {
|
|
||||||
push_any_value(name);
|
|
||||||
LuaSpecial classname(lua_gettop(L_));
|
|
||||||
makeclass(tab, classname);
|
|
||||||
lua_pop(L_, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rawequal(LuaSlot v1, LuaSlot v2) const {
|
bool rawequal(LuaSlot v1, LuaSlot v2) const {
|
||||||
return lua_rawequal(L_, v1, v2);
|
return lua_rawequal(L_, v1, v2);
|
||||||
@@ -475,21 +485,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lua flagbits manipulation: Table types.
|
// Lua flagbits manipulation: Table types.
|
||||||
enum TableType {
|
int gettabletype(LuaSlot tab) const;
|
||||||
TAB_GENERAL, // A general-purpose table.
|
void settabletype(LuaSlot tab, int t) const;
|
||||||
TAB_REGISTRY, // The registry table.
|
|
||||||
TAB_GLOBALENV, // The global environment table.
|
// If slot is a table, returns the LUA_TT_XXX table type.
|
||||||
TAB_TANGIBLE, // A tangible's database.
|
// If slot is not a table, returns the LUA_TXXX general type.
|
||||||
TAB_TANGIBLEMETA, // A tangible's metatable.
|
int xtype(LuaSlot slot) const;
|
||||||
TAB_GLOBALDB, // Part of the globaldb.
|
|
||||||
TAB_CLASS, // A class which is directly reachable from the global environment.
|
|
||||||
};
|
|
||||||
TableType gettabletype(LuaSlot tab);
|
|
||||||
void settabletype(LuaSlot tab, TableType t);
|
|
||||||
|
|
||||||
// Lua flagbits manipulation: visited bit.
|
// Lua flagbits manipulation: visited bit.
|
||||||
bool getvisited(LuaSlot tab);
|
bool getvisited(LuaSlot tab) const;
|
||||||
void setvisited(LuaSlot tab, bool visited);
|
void setvisited(LuaSlot tab, bool visited) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
259
luprex/core/cpp/tablecmp.cpp
Normal file
259
luprex/core/cpp/tablecmp.cpp
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
#include "luastack.hpp"
|
||||||
|
#include "streambuffer.hpp"
|
||||||
|
#include "tablecmp.hpp"
|
||||||
|
|
||||||
|
// Given a table and an tnmap, return the table number of the table.
|
||||||
|
// Returns zero if the table doesn't have a table number.
|
||||||
|
//
|
||||||
|
static int get_table_number(LuaStack &MLS, LuaSlot mval, LuaSlot mtnmap) {
|
||||||
|
lua_State *L = MLS.state();
|
||||||
|
lua_pushvalue(L, mval.index());
|
||||||
|
lua_rawget(L, mtnmap.index());
|
||||||
|
int result = 0;
|
||||||
|
if (lua_type(L, -1) == LUA_TNUMBER) {
|
||||||
|
result = lua_tointeger(L, -1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the tangible ID of a tangible.
|
||||||
|
static int64_t get_tangible_id(const LuaStack &LS, LuaSlot tab) {
|
||||||
|
int64_t id = 0;
|
||||||
|
if (LS.istable(tab) && LS.gettabletype(tab) == LUA_TT_TANGIBLE) {
|
||||||
|
lua_State *L = LS.state();
|
||||||
|
if (lua_getmetatable(L, tab.index())) {
|
||||||
|
lua_pushstring(L, "id");
|
||||||
|
lua_rawget(L, -2);
|
||||||
|
if (lua_type(L, -1) == LUA_TNUMBER) {
|
||||||
|
id = lua_tointeger(L, -1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool equivalent_values(LuaStack &MLS, LuaSlot mval, LuaSlot mtnmap,
|
||||||
|
LuaStack &SLS, LuaSlot sval, LuaSlot stnmap) {
|
||||||
|
switch (MLS.xtype(mval)) {
|
||||||
|
case LUA_TBOOLEAN: {
|
||||||
|
if (SLS.type(sval) != LUA_TBOOLEAN) return false;
|
||||||
|
return MLS.ckboolean(mval) == SLS.ckboolean(sval);
|
||||||
|
}
|
||||||
|
case LUA_TNUMBER: {
|
||||||
|
if (SLS.type(sval) != LUA_TNUMBER) return false;
|
||||||
|
return MLS.cknumber(mval) == SLS.cknumber(sval);
|
||||||
|
}
|
||||||
|
case LUA_TSTRING: {
|
||||||
|
// This could be faster if I used lua_tolstring directly.
|
||||||
|
if (SLS.type(sval) != LUA_TSTRING) return false;
|
||||||
|
return MLS.ckstring(mval) == SLS.ckstring(sval);
|
||||||
|
}
|
||||||
|
case LUA_TFUNCTION: {
|
||||||
|
// Cannot really compare. Just return true if the types match.
|
||||||
|
return SLS.type(sval) == MLS.type(mval);
|
||||||
|
}
|
||||||
|
case LUA_TT_GENERAL: {
|
||||||
|
if (SLS.xtype(sval) != LUA_TT_GENERAL) return false;
|
||||||
|
int midx = get_table_number(MLS, mval, mtnmap);
|
||||||
|
if (midx == 0) return false;
|
||||||
|
int sidx = get_table_number(SLS, sval, stnmap);
|
||||||
|
if (sidx == 0) return false;
|
||||||
|
return midx == sidx;
|
||||||
|
}
|
||||||
|
case LUA_TT_CLASS: {
|
||||||
|
if (SLS.xtype(sval) != LUA_TT_CLASS) return false;
|
||||||
|
return MLS.classname(mval) == SLS.classname(sval);
|
||||||
|
}
|
||||||
|
case LUA_TT_TANGIBLE: {
|
||||||
|
if (SLS.xtype(sval) != LUA_TT_TANGIBLE) return false;
|
||||||
|
return get_tangible_id(MLS, mval) == get_tangible_id(SLS, sval);
|
||||||
|
}
|
||||||
|
case LUA_TT_GLOBALENV: {
|
||||||
|
return (SLS.xtype(sval) == LUA_TT_GLOBALENV);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// We're forcing anything else to go to NIL,
|
||||||
|
// and once it's NIL, we consider it 'equal'.
|
||||||
|
return SLS.type(sval) == LUA_TNIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void transmit_value(LuaStack &MLS, LuaSlot mval, LuaSlot mtnmap, StreamBuffer *sb) {
|
||||||
|
switch (MLS.xtype(mval)) {
|
||||||
|
case LUA_TBOOLEAN: {
|
||||||
|
sb->write_uint8(LUA_TBOOLEAN);
|
||||||
|
sb->write_bool(MLS.ckboolean(mval));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TNUMBER: {
|
||||||
|
sb->write_uint8(LUA_TNUMBER);
|
||||||
|
sb->write_double(MLS.cknumber(mval));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TSTRING: {
|
||||||
|
sb->write_uint8(LUA_TSTRING);
|
||||||
|
sb->write_string(MLS.ckstring(mval));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_GENERAL: {
|
||||||
|
sb->write_uint8(LUA_TT_GENERAL);
|
||||||
|
sb->write_uint32(get_table_number(MLS, mval, mtnmap));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_CLASS: {
|
||||||
|
sb->write_uint8(LUA_TT_CLASS);
|
||||||
|
sb->write_string(MLS.classname(mval));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_TANGIBLE: {
|
||||||
|
sb->write_uint8(LUA_TT_TANGIBLE);
|
||||||
|
sb->write_int64(get_tangible_id(MLS, mval));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_GLOBALENV: {
|
||||||
|
sb->write_uint8(LUA_TT_GLOBALENV);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
sb->write_uint8(LUA_TNIL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tablecmp_diff(lua_State *synch, lua_State *master, bool cmeta, StreamBuffer *sb) {
|
||||||
|
LuaArg mtnmap, mtab, stnmap, stab;
|
||||||
|
LuaVar skey, mkey, sval, mval, mnil;
|
||||||
|
LuaStack SLS(synch, stnmap, stab, skey, sval);
|
||||||
|
LuaStack MLS(master, mtnmap, mtab, mkey, mval, mnil);
|
||||||
|
MLS.set(mnil, LuaNil);
|
||||||
|
int nupdates = 0;
|
||||||
|
|
||||||
|
sb->write_int32(0);
|
||||||
|
int wc = sb->total_writes();
|
||||||
|
|
||||||
|
MLS.set(mkey, LuaNil);
|
||||||
|
while (MLS.next(mtab, mkey, mval)) {
|
||||||
|
if (!MLS.issortablekey(mkey)) continue;
|
||||||
|
MLS.movesortablekey(mkey, SLS, skey);
|
||||||
|
SLS.rawget(sval, stab, skey);
|
||||||
|
if (!equivalent_values(MLS, mval, mtnmap, SLS, sval, stnmap)) {
|
||||||
|
transmit_value(MLS, mkey, mtnmap, sb);
|
||||||
|
transmit_value(MLS, mval, mtnmap, sb);
|
||||||
|
nupdates += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SLS.set(skey, LuaNil);
|
||||||
|
while (SLS.next(stab, skey, sval)) {
|
||||||
|
if (!SLS.issortablekey(skey)) continue;
|
||||||
|
SLS.movesortablekey(skey, MLS, mkey);
|
||||||
|
MLS.rawget(mval, mtab, mkey);
|
||||||
|
if (MLS.isnil(mval)) {
|
||||||
|
transmit_value(MLS, mkey, mtnmap, sb);
|
||||||
|
transmit_value(MLS, mval, mtnmap, sb);
|
||||||
|
nupdates += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmeta) {
|
||||||
|
SLS.getmetatable(sval, stab);
|
||||||
|
MLS.getmetatable(mval, mtab);
|
||||||
|
if (!equivalent_values(MLS, mval, mtnmap, SLS, sval, stnmap)) {
|
||||||
|
transmit_value(MLS, mnil, mtnmap, sb);
|
||||||
|
transmit_value(MLS, mval, mtnmap, sb);
|
||||||
|
nupdates += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sb->overwrite_int32(wc, nupdates);
|
||||||
|
|
||||||
|
SLS.result();
|
||||||
|
MLS.result();
|
||||||
|
return (nupdates > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tablecmp_value_debug_string(StreamBuffer *sb, std::ostringstream &oss) {
|
||||||
|
int kind = sb->read_uint8();
|
||||||
|
switch (kind) {
|
||||||
|
case LUA_TBOOLEAN: {
|
||||||
|
bool b = sb->read_bool();
|
||||||
|
oss << (b ? "true":"false");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TNUMBER: {
|
||||||
|
oss << sb->read_double();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TSTRING: {
|
||||||
|
oss << sb->read_string();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_GENERAL: {
|
||||||
|
oss << "table " << sb->read_int32();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_CLASS: {
|
||||||
|
oss << "class " << sb->read_string();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_TANGIBLE: {
|
||||||
|
oss << "tan " << sb->read_int64();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_GLOBALENV: {
|
||||||
|
oss << "globals";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TNIL: {
|
||||||
|
oss << "nil";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(false); // Should not get here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string tablecmp_debug_string(StreamBuffer *sb) {
|
||||||
|
std::vector<std::string> sorted;
|
||||||
|
std::ostringstream oss;
|
||||||
|
int ndiffs = sb->read_int32();
|
||||||
|
for (int i = 0; i < ndiffs; i++) {
|
||||||
|
tablecmp_value_debug_string(sb, oss);
|
||||||
|
oss << "=";
|
||||||
|
tablecmp_value_debug_string(sb, oss);
|
||||||
|
sorted.push_back(oss.str());
|
||||||
|
oss.str("");
|
||||||
|
}
|
||||||
|
std::sort(sorted.begin(), sorted.end());
|
||||||
|
for (const std::string &s : sorted) {
|
||||||
|
oss << s << ";";
|
||||||
|
}
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
LuaDefine(table_diffcompare, "c") {
|
||||||
|
LuaArg mtnmap, mtab, stnmap, stab;
|
||||||
|
LuaRet dbgstring;
|
||||||
|
LuaVar tthread;
|
||||||
|
LuaStack LS(L, mtnmap, mtab, stnmap, stab, dbgstring, tthread);
|
||||||
|
// Create a temporary thread to be the 'synch model'. We'll use the
|
||||||
|
// existing thread as the 'master model'.
|
||||||
|
lua_State *synch = lua_newthread(L);
|
||||||
|
lua_replace(L, tthread.index());
|
||||||
|
// Call tablecmp_diff.
|
||||||
|
StreamBuffer sb;
|
||||||
|
lua_pushvalue(L, stnmap.index());
|
||||||
|
lua_pushvalue(L, stab.index());
|
||||||
|
lua_xmove(L, synch, 2);
|
||||||
|
lua_pushvalue(L, mtnmap.index());
|
||||||
|
lua_pushvalue(L, mtab.index());
|
||||||
|
tablecmp_diff(synch, L, true, &sb);
|
||||||
|
// Convert the output to a debug string.
|
||||||
|
LS.set(dbgstring, tablecmp_debug_string(&sb));
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
29
luprex/core/cpp/tablecmp.hpp
Normal file
29
luprex/core/cpp/tablecmp.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// tablecmp -- compare two tables nonrecursively.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef TABLECMP_HPP
|
||||||
|
#define TABLECMP_HPP
|
||||||
|
|
||||||
|
#include "luastack.hpp"
|
||||||
|
#include "util.hpp"
|
||||||
|
#include "streambuffer.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
// Compare two tables, generating a diff in the specified stream buffer.
|
||||||
|
//
|
||||||
|
// The synch stack must have stnmap, stab on top.
|
||||||
|
// The master stack must have mtnmap, mtab on top.
|
||||||
|
// If cmeta is true, the metatables of the two tables are compared.
|
||||||
|
// Returns true if there were any diffs.
|
||||||
|
//
|
||||||
|
bool tablecmp_diff(lua_State *synch, lua_State *master, bool cmeta, StreamBuffer *sb);
|
||||||
|
|
||||||
|
// Given a tablecmp_diff output, convert it to a debug string.
|
||||||
|
//
|
||||||
|
std::string tablecmp_debug_string(StreamBuffer *sb);
|
||||||
|
|
||||||
|
#endif // TABLECMP_HPP
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "animqueue.hpp"
|
#include "animqueue.hpp"
|
||||||
#include "gui.hpp"
|
#include "gui.hpp"
|
||||||
#include "traceback.hpp"
|
#include "traceback.hpp"
|
||||||
|
#include "tablecmp.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
void World::store_global_pointer(lua_State *L, World *v) {
|
void World::store_global_pointer(lua_State *L, World *v) {
|
||||||
@@ -48,11 +49,11 @@ World::World(util::WorldType wt) {
|
|||||||
Gui::store_global_pointer(state(), nullptr);
|
Gui::store_global_pointer(state(), nullptr);
|
||||||
|
|
||||||
// Set the tabletype of the registry.
|
// Set the tabletype of the registry.
|
||||||
LS.settabletype(LuaRegistry, LuaStack::TAB_REGISTRY);
|
LS.settabletype(LuaRegistry, LUA_TT_REGISTRY);
|
||||||
|
|
||||||
// Set the tabletype of the global environment.
|
// Set the tabletype of the global environment.
|
||||||
LS.getglobaltable(globtab);
|
LS.getglobaltable(globtab);
|
||||||
LS.settabletype(globtab, LuaStack::TAB_GLOBALENV);
|
LS.settabletype(globtab, LUA_TT_GLOBALENV);
|
||||||
|
|
||||||
// Create the tangibles table in the registry.
|
// Create the tangibles table in the registry.
|
||||||
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
||||||
@@ -114,25 +115,34 @@ World::TanVector World::tangible_get_all(const IdVector &ids) const {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tangible *World::tangible_get(lua_State *L, int idx) {
|
Tangible *World::tangible_get(const LuaStack &LS, LuaSlot tab) {
|
||||||
Tangible *result = nullptr;
|
int64_t id = tangible_id(LS, tab);
|
||||||
int top = lua_gettop(L);
|
if (id == 0) {
|
||||||
if (lua_istable(L, idx)) {
|
luaL_error(LS.state(), "parameter is not a tangible");
|
||||||
lua_getmetatable(L, idx);
|
|
||||||
if (lua_istable(L, -1)) {
|
|
||||||
lua_pushstring(L, "id");
|
|
||||||
lua_rawget(L, -2);
|
|
||||||
lua_Number id = lua_tonumber(L, -1);
|
|
||||||
result = tangible_get(int64_t(id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
lua_settop(L, top);
|
Tangible *result = tangible_get(id);
|
||||||
if (result == nullptr) {
|
if (result == nullptr) {
|
||||||
luaL_error(L, "parameter is not a tangible");
|
luaL_error(LS.state(), "parameter is not a tangible");
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t World::tangible_id(const LuaStack &LS, LuaSlot tab) {
|
||||||
|
int64_t id = 0;
|
||||||
|
if (LS.istable(tab) && LS.gettabletype(tab) == LUA_TT_TANGIBLE) {
|
||||||
|
lua_State *L = LS.state();
|
||||||
|
if (lua_getmetatable(L, tab.index())) {
|
||||||
|
lua_pushstring(L, "id");
|
||||||
|
lua_rawget(L, -2);
|
||||||
|
if (lua_type(L, -1) == LUA_TNUMBER) {
|
||||||
|
id = lua_tointeger(L, -1);
|
||||||
|
}
|
||||||
|
lua_pop(L, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
void World::tangible_delete(int64_t id) {
|
void World::tangible_delete(int64_t id) {
|
||||||
lua_State *L = state();
|
lua_State *L = state();
|
||||||
LuaVar tangibles, database;
|
LuaVar tangibles, database;
|
||||||
@@ -153,6 +163,7 @@ void World::tangible_delete(int64_t id) {
|
|||||||
// Clear out the database.
|
// Clear out the database.
|
||||||
LS.clearmetatable(database);
|
LS.clearmetatable(database);
|
||||||
LS.cleartable(database);
|
LS.cleartable(database);
|
||||||
|
LS.settabletype(database, LUA_TT_DEADTANGIBLE);
|
||||||
|
|
||||||
// Remove the lua portion from the tangibles table.
|
// Remove the lua portion from the tangibles table.
|
||||||
LS.rawset(tangibles, id, LuaNil);
|
LS.rawset(tangibles, id, LuaNil);
|
||||||
@@ -221,7 +232,7 @@ std::string World::numbered_tables_debug_string() const {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string World::paired_tables_debug_string(lua_State *master, const TablePairing *pairing) const {
|
std::string World::paired_tables_debug_string(lua_State *master) const {
|
||||||
lua_State *synch = state();
|
lua_State *synch = state();
|
||||||
LuaVar mntmap, sntmap, mtab, stab, mtid, stid;
|
LuaVar mntmap, sntmap, mtab, stab, mtid, stid;
|
||||||
LuaStack MLS(master, mntmap, mtab, mtid);
|
LuaStack MLS(master, mntmap, mtab, mtid);
|
||||||
@@ -232,26 +243,26 @@ std::string World::paired_tables_debug_string(lua_State *master, const TablePair
|
|||||||
// Fetch the numbered tables map.
|
// Fetch the numbered tables map.
|
||||||
MLS.rawget(mntmap, LuaRegistry, "ntmap");
|
MLS.rawget(mntmap, LuaRegistry, "ntmap");
|
||||||
SLS.rawget(sntmap, LuaRegistry, "ntmap");
|
SLS.rawget(sntmap, LuaRegistry, "ntmap");
|
||||||
|
int m_ntables = MLS.rawlen(mntmap);
|
||||||
|
int s_ntables = MLS.rawlen(sntmap);
|
||||||
|
assert(m_ntables == s_ntables);
|
||||||
|
|
||||||
for (int i = 1; i < pairing->mpair.size(); i++) {
|
for (int i = 1; i <= m_ntables; i++) {
|
||||||
if (pairing->mpair[i] == 0) continue;
|
|
||||||
MLS.rawget(mtab, mntmap, i);
|
MLS.rawget(mtab, mntmap, i);
|
||||||
SLS.rawget(stab, sntmap, pairing->mpair[i]);
|
SLS.rawget(stab, sntmap, i);
|
||||||
std::string mname = "unknown";
|
if (MLS.istable(mtab) && SLS.istable(stab)) {
|
||||||
std::string sname = "unknown";
|
std::string mname = "unknown";
|
||||||
if (MLS.istable(mtab)) {
|
std::string sname = "unknown";
|
||||||
MLS.rawget(mtid, mtab, "TID");
|
MLS.rawget(mtid, mtab, "TID");
|
||||||
if (MLS.isstring(mtid)) {
|
if (MLS.isstring(mtid)) {
|
||||||
mname = MLS.ckstring(mtid);
|
mname = MLS.ckstring(mtid);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (SLS.istable(stab)) {
|
|
||||||
SLS.rawget(stid, stab, "TID");
|
SLS.rawget(stid, stab, "TID");
|
||||||
if (SLS.isstring(stid)) {
|
if (SLS.isstring(stid)) {
|
||||||
sname = SLS.ckstring(stid);
|
sname = SLS.ckstring(stid);
|
||||||
}
|
}
|
||||||
|
result.push_back(std::make_pair(mname, sname));
|
||||||
}
|
}
|
||||||
result.push_back(std::make_pair(mname, sname));
|
|
||||||
}
|
}
|
||||||
MLS.result();
|
MLS.result();
|
||||||
SLS.result();
|
SLS.result();
|
||||||
@@ -376,8 +387,8 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) {
|
|||||||
LS.setmetatable(database, metatab);
|
LS.setmetatable(database, metatab);
|
||||||
|
|
||||||
// Mark the tangible using the tabletype field.
|
// Mark the tangible using the tabletype field.
|
||||||
LS.settabletype(database, LuaStack::TAB_TANGIBLE);
|
LS.settabletype(database, LUA_TT_TANGIBLE);
|
||||||
LS.settabletype(metatab, LuaStack::TAB_TANGIBLEMETA);
|
LS.settabletype(metatab, LUA_TT_TANGIBLEMETA);
|
||||||
|
|
||||||
// Store the database into the tangibles table.
|
// Store the database into the tangibles table.
|
||||||
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
||||||
@@ -741,13 +752,11 @@ void World::difference_transmit(int64_t actor_id, World *master, StreamBuffer *s
|
|||||||
// Number tables in both the master and the synchronous model.
|
// Number tables in both the master and the synchronous model.
|
||||||
// Meanwhile, the client will number tables in the client-synchronous model.
|
// Meanwhile, the client will number tables in the client-synchronous model.
|
||||||
master->number_lua_tables(closetans);
|
master->number_lua_tables(closetans);
|
||||||
int s_ntables = number_lua_tables(closetans);
|
number_lua_tables(closetans);
|
||||||
|
|
||||||
// Pair tables from the synchronous and master models.
|
// Pair tables from the synchronous and master models.
|
||||||
TablePairing pairing;
|
pair_lua_tables(closetans, master->state());
|
||||||
pair_lua_tables(closetans, master->state(), &pairing);
|
int ncreate = pair_new_tables(closetans, master->state());
|
||||||
int ncreate = pair_new_tables(&pairing);
|
|
||||||
sb->write_int32(s_ntables);
|
|
||||||
sb->write_int32(ncreate);
|
sb->write_int32(ncreate);
|
||||||
create_new_tables(ncreate);
|
create_new_tables(ncreate);
|
||||||
|
|
||||||
@@ -761,8 +770,7 @@ 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);
|
number_lua_tables(closetans);
|
||||||
assert(s_ntables == sb->read_int32());
|
|
||||||
int ncreate = sb->read_int32();
|
int ncreate = sb->read_int32();
|
||||||
create_new_tables(ncreate);
|
create_new_tables(ncreate);
|
||||||
}
|
}
|
||||||
@@ -890,19 +898,12 @@ int World::number_lua_tables(const IdVector &basis) {
|
|||||||
for (int64_t id : basis) {
|
for (int64_t id : basis) {
|
||||||
LS.rawget(tab, tangibles, id);
|
LS.rawget(tab, tangibles, id);
|
||||||
assert(LS.istable(tab));
|
assert(LS.istable(tab));
|
||||||
// Maybe I should traverse the metatable?
|
|
||||||
// Traverse subtables.
|
// Traverse subtables.
|
||||||
LS.set(key, LuaNil);
|
LS.set(key, LuaNil);
|
||||||
while (LS.next(tab, key, val)) {
|
while (LS.next(tab, key, val)) {
|
||||||
// if (LS.isstring(key)) {
|
if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) {
|
||||||
// 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_checkstack(L, 10);
|
||||||
lua_pushvalue(L, val.index());
|
lua_pushvalue(L, val.index());
|
||||||
// std::cerr << "Stack: " << lua_gettop(L) - top << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -910,7 +911,6 @@ int World::number_lua_tables(const IdVector &basis) {
|
|||||||
// Pop tables from the stack one by one. If the table is not
|
// Pop tables from the stack one by one. If the table is not
|
||||||
// already numbered, number it and push subtables onto the stack.
|
// already numbered, number it and push subtables onto the stack.
|
||||||
while (lua_gettop(L) > top) {
|
while (lua_gettop(L) > top) {
|
||||||
//std::cerr << "Popping stack with " << lua_gettop(L) - top << std::endl;
|
|
||||||
lua_replace(L, tab.index());
|
lua_replace(L, tab.index());
|
||||||
LS.rawget(xid, tnmap, tab);
|
LS.rawget(xid, tnmap, tab);
|
||||||
if (LS.isnil(xid)) {
|
if (LS.isnil(xid)) {
|
||||||
@@ -919,22 +919,16 @@ int World::number_lua_tables(const IdVector &basis) {
|
|||||||
LS.rawset(ntmap, id, tab);
|
LS.rawset(ntmap, id, tab);
|
||||||
// Traverse the metatable.
|
// Traverse the metatable.
|
||||||
LS.getmetatable(val, tab);
|
LS.getmetatable(val, tab);
|
||||||
if (LS.istable(val) && LS.gettabletype(val)==LuaStack::TAB_GENERAL) {
|
if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) {
|
||||||
lua_checkstack(L, 10);
|
lua_checkstack(L, 10);
|
||||||
lua_pushvalue(L, val.index());
|
lua_pushvalue(L, val.index());
|
||||||
}
|
}
|
||||||
// Traverse the subtables.
|
// Traverse the subtables.
|
||||||
LS.set(key, LuaNil);
|
LS.set(key, LuaNil);
|
||||||
while (LS.next(tab, key, val)) {
|
while (LS.next(tab, key, val)) {
|
||||||
// if (LS.isstring(key)) {
|
if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) {
|
||||||
// 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_checkstack(L, 10);
|
||||||
lua_pushvalue(L, val.index());
|
lua_pushvalue(L, val.index());
|
||||||
// std::cerr << "Stack: " << lua_gettop(L) - top << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -952,24 +946,37 @@ void World::unnumber_lua_tables() {
|
|||||||
LS.rawset(LuaRegistry, "ntmap", LuaNil);
|
LS.rawset(LuaRegistry, "ntmap", LuaNil);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::pair_lua_tables(const IdVector &basis, lua_State *master, TablePairing *pairing) {
|
void World::pair_lua_tables(const IdVector &basis, lua_State *master) {
|
||||||
lua_State *synch = state();
|
lua_State *synch = state();
|
||||||
LuaVar stangibles, mtangibles, sntmap, mntmap, stnmap, mtnmap, stab, mtab, skey, mkey, sval, mval, sidx, midx;
|
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 SLS(synch, stangibles, stab, skey, sval, sntmap, stnmap, sidx);
|
||||||
LuaStack MLS(master, mtangibles, mtab, mkey, mval, mntmap, mtnmap, midx);
|
LuaStack MLS(master, mtangibles, mtab, mkey, mval, mntmap, mtnmap, midx);
|
||||||
|
|
||||||
|
// Fetch the tangible databases
|
||||||
SLS.rawget(stangibles, LuaRegistry, "tangibles");
|
SLS.rawget(stangibles, LuaRegistry, "tangibles");
|
||||||
MLS.rawget(mtangibles, LuaRegistry, "tangibles");
|
MLS.rawget(mtangibles, LuaRegistry, "tangibles");
|
||||||
|
|
||||||
|
// Fetch the synchronous model tnmap and ntmap
|
||||||
SLS.rawget(stnmap, LuaRegistry, "tnmap");
|
SLS.rawget(stnmap, LuaRegistry, "tnmap");
|
||||||
MLS.rawget(mtnmap, LuaRegistry, "tnmap");
|
|
||||||
SLS.rawget(sntmap, LuaRegistry, "ntmap");
|
SLS.rawget(sntmap, LuaRegistry, "ntmap");
|
||||||
MLS.rawget(mntmap, LuaRegistry, "ntmap");
|
|
||||||
assert(SLS.istable(stnmap));
|
assert(SLS.istable(stnmap));
|
||||||
assert(MLS.istable(mtnmap));
|
|
||||||
assert(SLS.istable(sntmap));
|
assert(SLS.istable(sntmap));
|
||||||
assert(MLS.istable(mntmap));
|
|
||||||
pairing->mpair.assign(MLS.rawlen(mntmap) + 1, 0);
|
// Initialize the master model tnmap and ntmap
|
||||||
pairing->spair.assign(SLS.rawlen(sntmap) + 1, 0);
|
MLS.set(mtnmap, LuaNewTable);
|
||||||
|
MLS.set(mntmap, LuaNewTable);
|
||||||
|
MLS.rawset(LuaRegistry, "tnmap", mtnmap);
|
||||||
|
MLS.rawset(LuaRegistry, "ntmap", mntmap);
|
||||||
|
int s_ntables = SLS.rawlen(sntmap);
|
||||||
|
for (int i = 1; i <= s_ntables; i++) {
|
||||||
|
MLS.rawset(mntmap, i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of which tables are already paired
|
||||||
|
std::vector<bool> paired;
|
||||||
|
paired.assign(s_ntables + 1, false);
|
||||||
|
|
||||||
|
// This records the top of the stack.
|
||||||
int mtop = lua_gettop(master);
|
int mtop = lua_gettop(master);
|
||||||
|
|
||||||
for (int64_t id : basis) {
|
for (int64_t id : basis) {
|
||||||
@@ -981,8 +988,7 @@ void World::pair_lua_tables(const IdVector &basis, lua_State *master, TablePairi
|
|||||||
while (MLS.next(mtab, mkey, mval)) {
|
while (MLS.next(mtab, mkey, mval)) {
|
||||||
if (!MLS.issortablekey(mkey)) continue;
|
if (!MLS.issortablekey(mkey)) continue;
|
||||||
if (!MLS.istable(mval)) continue;
|
if (!MLS.istable(mval)) continue;
|
||||||
MLS.movesortablekey(mkey, synch);
|
MLS.movesortablekey(mkey, SLS, skey);
|
||||||
lua_replace(synch, skey.index());
|
|
||||||
SLS.rawget(sval, stab, skey);
|
SLS.rawget(sval, stab, skey);
|
||||||
if (!SLS.istable(sval)) continue;
|
if (!SLS.istable(sval)) continue;
|
||||||
lua_checkstack(master, 20);
|
lua_checkstack(master, 20);
|
||||||
@@ -995,17 +1001,23 @@ void World::pair_lua_tables(const IdVector &basis, lua_State *master, TablePairi
|
|||||||
while (lua_gettop(master) > mtop) {
|
while (lua_gettop(master) > mtop) {
|
||||||
lua_replace(master, mtab.index());
|
lua_replace(master, mtab.index());
|
||||||
lua_replace(synch, stab.index());
|
lua_replace(synch, stab.index());
|
||||||
|
// If the master table is not a general table, skip.
|
||||||
|
if (MLS.gettabletype(mtab) != LUA_TT_GENERAL) continue;
|
||||||
|
// 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.
|
||||||
SLS.rawget(sidx, stnmap, stab);
|
SLS.rawget(sidx, stnmap, stab);
|
||||||
if (!SLS.isnumber(sidx)) continue;
|
if (!SLS.isnumber(sidx)) continue;
|
||||||
int imidx = MLS.ckint(midx);
|
int idx = SLS.ckinteger(sidx);
|
||||||
int isidx = SLS.ckint(sidx);
|
assert((idx >= 1) && (idx <= s_ntables));
|
||||||
if (pairing->spair[isidx] != 0) continue;
|
// Pair the tables.
|
||||||
if (pairing->mpair[imidx] != 0) continue;
|
MLS.rawset(mtnmap, mtab, idx);
|
||||||
pairing->spair[isidx] = imidx;
|
MLS.rawset(mntmap, idx, mtab);
|
||||||
pairing->mpair[imidx] = isidx;
|
paired[idx] = true;
|
||||||
// Pair the metatables.
|
// Potentially pair the metatables.
|
||||||
MLS.getmetatable(mval, mtab);
|
MLS.getmetatable(mval, mtab);
|
||||||
if (MLS.istable(mval)) {
|
if (MLS.istable(mval)) {
|
||||||
SLS.getmetatable(sval, stab);
|
SLS.getmetatable(sval, stab);
|
||||||
@@ -1019,8 +1031,7 @@ void World::pair_lua_tables(const IdVector &basis, lua_State *master, TablePairi
|
|||||||
while (MLS.next(mtab, mkey, mval)) {
|
while (MLS.next(mtab, mkey, mval)) {
|
||||||
if (!MLS.issortablekey(mkey)) continue;
|
if (!MLS.issortablekey(mkey)) continue;
|
||||||
if (!MLS.istable(mval)) continue;
|
if (!MLS.istable(mval)) continue;
|
||||||
MLS.movesortablekey(mkey, synch);
|
MLS.movesortablekey(mkey, SLS, skey);
|
||||||
lua_replace(synch, skey.index());
|
|
||||||
SLS.rawget(sval, stab, skey);
|
SLS.rawget(sval, stab, skey);
|
||||||
if (!SLS.istable(sval)) continue;
|
if (!SLS.istable(sval)) continue;
|
||||||
lua_checkstack(master, 20);
|
lua_checkstack(master, 20);
|
||||||
@@ -1034,16 +1045,71 @@ void World::pair_lua_tables(const IdVector &basis, lua_State *master, TablePairi
|
|||||||
SLS.result();
|
SLS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
int World::pair_new_tables(TablePairing *pairing) {
|
int World::pair_new_tables(const IdVector &basis, lua_State *master) {
|
||||||
int orig = pairing->spair.size();
|
// This is conceptually recursive, but we're going to use an
|
||||||
for (int i = 1; i <= pairing->mpair.size(); i++) {
|
// explicit stack (the lua stack).
|
||||||
int id = pairing->mpair[i];
|
lua_State *L = master;
|
||||||
if (id == 0) {
|
LuaVar tnmap, ntmap, tangibles, tab, key, val, xid;
|
||||||
pairing->mpair[i] = pairing->spair.size();
|
LuaStack LS(L, tnmap, ntmap, tangibles, tab, key, val, xid);
|
||||||
pairing->spair.push_back(i);
|
LS.rawget(tnmap, LuaRegistry, "tnmap");
|
||||||
|
LS.rawget(ntmap, LuaRegistry, "ntmap");
|
||||||
|
LS.rawget(tangibles, LuaRegistry, "tangibles");
|
||||||
|
int ntables = LS.rawlen(ntmap);
|
||||||
|
std::vector<bool> visited;
|
||||||
|
visited.assign(ntables + 1, false);
|
||||||
|
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));
|
||||||
|
LS.set(key, LuaNil);
|
||||||
|
while (LS.next(tab, key, val)) {
|
||||||
|
if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) {
|
||||||
|
lua_checkstack(L, 10);
|
||||||
|
lua_pushvalue(L, val.index());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pairing->spair.size() - orig;
|
|
||||||
|
// Pop tables from the stack one by one. If the table is not
|
||||||
|
// numbered, number it. If it is not visited, visit it.
|
||||||
|
while (lua_gettop(L) > top) {
|
||||||
|
lua_replace(L, tab.index());
|
||||||
|
int id = 0;
|
||||||
|
LS.rawget(xid, tnmap, tab);
|
||||||
|
if (!LS.isnumber(xid)) {
|
||||||
|
id = visited.size();
|
||||||
|
LS.rawset(tnmap, tab, id);
|
||||||
|
LS.rawset(ntmap, id, tab);
|
||||||
|
visited.push_back(false);
|
||||||
|
} else {
|
||||||
|
id = LS.cknumber(xid);
|
||||||
|
assert((id >= 0) && (id < int(visited.size())));
|
||||||
|
}
|
||||||
|
if (!visited[id]) {
|
||||||
|
visited[id] = true;
|
||||||
|
// Traverse the metatable.
|
||||||
|
LS.getmetatable(val, tab);
|
||||||
|
if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_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.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) {
|
||||||
|
lua_checkstack(L, 10);
|
||||||
|
lua_pushvalue(L, val.index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LS.result();
|
||||||
|
assert(stack_is_clear());
|
||||||
|
return visited.size() - 1 - ntables;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::create_new_tables(int n) {
|
void World::create_new_tables(int n) {
|
||||||
@@ -1065,12 +1131,87 @@ void World::create_new_tables(int n) {
|
|||||||
assert(stack_is_clear());
|
assert(stack_is_clear());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void World::diff_lua_tables(lua_State *master, StreamBuffer *sb) {
|
||||||
|
lua_State *synch = state();
|
||||||
|
LuaVar sntmap, mntmap, stnmap, mtnmap;
|
||||||
|
LuaStack SLS(synch, sntmap, stnmap);
|
||||||
|
LuaStack MLS(master, mntmap, mtnmap);
|
||||||
|
SLS.rawget(sntmap, LuaRegistry, "ntmap");
|
||||||
|
MLS.rawget(mntmap, LuaRegistry, "ntmap");
|
||||||
|
SLS.rawget(stnmap, LuaRegistry, "tnmap");
|
||||||
|
MLS.rawget(mtnmap, LuaRegistry, "tnmap");
|
||||||
|
int m_ntables = MLS.rawlen(mntmap);
|
||||||
|
int s_ntables = SLS.rawlen(sntmap);
|
||||||
|
assert(m_ntables == s_ntables);
|
||||||
|
|
||||||
|
sb->write_int32(0);
|
||||||
|
int write_count_after = sb->total_writes();
|
||||||
|
int nmodified = 0;
|
||||||
|
int s_top = lua_gettop(synch);
|
||||||
|
int m_top = lua_gettop(master);
|
||||||
|
for (int id = 1; id <= m_ntables; id++) {
|
||||||
|
lua_pushvalue(master, mtnmap.index());
|
||||||
|
lua_rawgeti(master, mtnmap.index(), id);
|
||||||
|
if (lua_type(master, -1) == LUA_TTABLE) {
|
||||||
|
lua_pushvalue(synch, stnmap.index());
|
||||||
|
lua_rawgeti(synch, sntmap.index(), id);
|
||||||
|
int tw = sb->total_writes();
|
||||||
|
sb->write_int32(id);
|
||||||
|
nmodified += 1;
|
||||||
|
if (!tablecmp_diff(synch, master, true, sb)) {
|
||||||
|
sb->unwrite_to(tw);
|
||||||
|
nmodified -= 1;
|
||||||
|
}
|
||||||
|
assert(lua_gettop(synch) == s_top);
|
||||||
|
assert(lua_gettop(master) == m_top);
|
||||||
|
} else {
|
||||||
|
lua_pop(master, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb->overwrite_int32(write_count_after, nmodified);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void World::diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb) {
|
||||||
|
lua_State *synch = state();
|
||||||
|
LuaVar stnmap, mtnmap, stangibles, mtangibles;
|
||||||
|
LuaStack SLS(synch, stnmap, stangibles);
|
||||||
|
LuaStack MLS(master, mtnmap, mtangibles);
|
||||||
|
SLS.rawget(stnmap, LuaRegistry, "tnmap");
|
||||||
|
MLS.rawget(mtnmap, LuaRegistry, "tnmap");
|
||||||
|
SLS.rawget(stangibles, LuaRegistry, "tangibles");
|
||||||
|
MLS.rawget(mtangibles, LuaRegistry, "tangibles");
|
||||||
|
|
||||||
|
sb->write_int32(0);
|
||||||
|
int write_count_after = sb->total_writes();
|
||||||
|
int nmodified = 0;
|
||||||
|
int s_top = lua_gettop(synch);
|
||||||
|
int m_top = lua_gettop(master);
|
||||||
|
for (int64_t id : basis) {
|
||||||
|
lua_pushvalue(master, mtnmap.index());
|
||||||
|
lua_rawgeti(master, mtangibles.index(), id);
|
||||||
|
lua_pushvalue(synch, stnmap.index());
|
||||||
|
lua_rawgeti(synch, stangibles.index(), id);
|
||||||
|
int tw = sb->total_writes();
|
||||||
|
sb->write_int64(id);
|
||||||
|
nmodified += 1;
|
||||||
|
if (!tablecmp_diff(synch, master, false, sb)) {
|
||||||
|
sb->unwrite_to(tw);
|
||||||
|
nmodified -= 1;
|
||||||
|
}
|
||||||
|
assert(lua_gettop(synch) == s_top);
|
||||||
|
assert(lua_gettop(master) == m_top);
|
||||||
|
}
|
||||||
|
sb->overwrite_int32(write_count_after, nmodified);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
LuaStack LS(L, tanobj, graphic, plane, x, y, z, facing);
|
LuaStack LS(L, tanobj, graphic, plane, x, y, z, facing);
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
Tangible *tan = w->tangible_get(L, tanobj.index());
|
Tangible *tan = w->tangible_get(LS, tanobj);
|
||||||
const AnimStep &aqback = tan->anim_queue_.back();
|
const AnimStep &aqback = tan->anim_queue_.back();
|
||||||
LS.set(graphic, aqback.graphic());
|
LS.set(graphic, aqback.graphic());
|
||||||
LS.set(plane, aqback.plane());
|
LS.set(plane, aqback.plane());
|
||||||
@@ -1085,7 +1226,7 @@ LuaDefine(tangible_animate, "c") {
|
|||||||
LuaArg tanobj, config;
|
LuaArg tanobj, config;
|
||||||
LuaStack LS(L, tanobj, config);
|
LuaStack LS(L, tanobj, config);
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
Tangible *tan = w->tangible_get(L, tanobj.index());
|
Tangible *tan = w->tangible_get(LS, tanobj);
|
||||||
int64_t id = w->id_global_pool_.alloc_id_for_thread(L);
|
int64_t id = w->id_global_pool_.alloc_id_for_thread(L);
|
||||||
const AnimStep &prev = tan->anim_queue_.back();
|
const AnimStep &prev = tan->anim_queue_.back();
|
||||||
AnimStep step;
|
AnimStep step;
|
||||||
@@ -1103,7 +1244,7 @@ LuaDefine(tangible_setclass, "c") {
|
|||||||
LuaVar classtab, mt;
|
LuaVar classtab, mt;
|
||||||
LuaStack LS(L, tanobj, classname, classtab, mt);
|
LuaStack LS(L, tanobj, classname, classtab, mt);
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
w->tangible_get(L, tanobj.index());
|
w->tangible_get(LS, tanobj);
|
||||||
LS.getclass(classtab, classname);
|
LS.getclass(classtab, classname);
|
||||||
LS.getmetatable(mt, tanobj);
|
LS.getmetatable(mt, tanobj);
|
||||||
LS.rawset(mt, "__index", classtab);
|
LS.rawset(mt, "__index", classtab);
|
||||||
@@ -1114,7 +1255,7 @@ LuaDefine(tangible_delete, "c") {
|
|||||||
LuaArg tanobj;
|
LuaArg tanobj;
|
||||||
LuaStack LS(L, tanobj);
|
LuaStack LS(L, tanobj);
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
Tangible *tan = w->tangible_get(L, tanobj.index());
|
Tangible *tan = w->tangible_get(LS, tanobj);
|
||||||
assert(tan != nullptr); // this should be checked above.
|
assert(tan != nullptr); // this should be checked above.
|
||||||
if (tan->is_an_actor()) {
|
if (tan->is_an_actor()) {
|
||||||
luaL_error(L, "Cannot delete a player using tangible.delete, use tangible.redirect instead.");
|
luaL_error(L, "Cannot delete a player using tangible.delete, use tangible.redirect instead.");
|
||||||
@@ -1188,14 +1329,14 @@ LuaDefine(tangible_redirect, "c") {
|
|||||||
LuaStack LS(L, actor1, actor2, bldz);
|
LuaStack LS(L, actor1, actor2, bldz);
|
||||||
World *w = World::fetch_global_pointer(L);
|
World *w = World::fetch_global_pointer(L);
|
||||||
bool bulldoze = LS.ckboolean(bldz);
|
bool bulldoze = LS.ckboolean(bldz);
|
||||||
Tangible *tan1 = w->tangible_get(L, actor1.index());
|
Tangible *tan1 = w->tangible_get(LS, actor1);
|
||||||
if (!tan1->is_an_actor()) {
|
if (!tan1->is_an_actor()) {
|
||||||
luaL_error(L, "redirect source is not an actor");
|
luaL_error(L, "redirect source is not an actor");
|
||||||
}
|
}
|
||||||
if (LS.isnil(actor2)) {
|
if (LS.isnil(actor2)) {
|
||||||
w->redirects_[tan1->id()] = 0;
|
w->redirects_[tan1->id()] = 0;
|
||||||
} else {
|
} else {
|
||||||
Tangible *tan2 = w->tangible_get(L, actor2.index());
|
Tangible *tan2 = w->tangible_get(LS, actor2);
|
||||||
tan2->configure_id_pool_for_actor();
|
tan2->configure_id_pool_for_actor();
|
||||||
w->redirects_[tan1->id()] = tan2->id();
|
w->redirects_[tan1->id()] = tan2->id();
|
||||||
}
|
}
|
||||||
@@ -1205,6 +1346,14 @@ LuaDefine(tangible_redirect, "c") {
|
|||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LuaDefine(tangible_id, "c") {
|
||||||
|
LuaArg tanobj;
|
||||||
|
LuaRet id;
|
||||||
|
LuaStack LS(L, tanobj, id);
|
||||||
|
LS.set(id, World::tangible_id(LS, tanobj));
|
||||||
|
return LS.result();
|
||||||
|
}
|
||||||
|
|
||||||
LuaDefine(world_wait, "f") {
|
LuaDefine(world_wait, "f") {
|
||||||
if ((lua_gettop(L) != 1) || (lua_type(L, -1) != LUA_TNUMBER)) {
|
if ((lua_gettop(L) != 1) || (lua_type(L, -1) != LUA_TNUMBER)) {
|
||||||
luaL_error(L, "Argument to wait must be a number.");
|
luaL_error(L, "Argument to wait must be a number.");
|
||||||
@@ -1217,16 +1366,12 @@ LuaDefine(world_getregistry, "f") {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDefine(world_gettabletype, "f") {
|
LuaDefine(world_xtype, "f") {
|
||||||
LuaArg tab;
|
LuaArg tab;
|
||||||
LuaRet ttype;
|
LuaRet rtype;
|
||||||
LuaStack LS(L, tab, ttype);
|
LuaStack LS(L, tab, rtype);
|
||||||
if (LS.istable(tab)) {
|
int xt = LS.xtype(tab);
|
||||||
LuaStack::TableType tt = LS.gettabletype(tab);
|
LS.set(rtype, xt);
|
||||||
LS.set(ttype, int(tt));
|
|
||||||
} else {
|
|
||||||
luaL_error(L, "Not a table");
|
|
||||||
}
|
|
||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1237,10 +1382,10 @@ LuaDefine(world_settabletype, "f") {
|
|||||||
luaL_error(L, "Not a table");
|
luaL_error(L, "Not a table");
|
||||||
}
|
}
|
||||||
int tt = LS.ckinteger(ttype);
|
int tt = LS.ckinteger(ttype);
|
||||||
if ((tt < 0) || (tt > 15)) {
|
if ((tt < LUA_TT_GENERAL) || (tt > LUA_TT_CLASS)) {
|
||||||
luaL_error(L, "table type out of range");
|
luaL_error(L, "table type out of range");
|
||||||
}
|
}
|
||||||
LS.settabletype(tab, LuaStack::TableType(tt));
|
LS.settabletype(tab, tt);
|
||||||
return LS.result();
|
return LS.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1255,7 +1400,7 @@ 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;
|
int ncreate;
|
||||||
|
|
||||||
// Test the numbering of lua tables. We create some general
|
// Test the numbering of lua tables. We create some general
|
||||||
// tables using tangible_set_string. Then we install some
|
// tables using tangible_set_string. Then we install some
|
||||||
@@ -1270,8 +1415,7 @@ LuaDefine(unittests_world, "c") {
|
|||||||
m->tangible_set_string(123, "inventory.cplx.TID", "inventory.cplx");
|
m->tangible_set_string(123, "inventory.cplx.TID", "inventory.cplx");
|
||||||
m->tangible_copy_global(123, "math", "math");
|
m->tangible_copy_global(123, "math", "math");
|
||||||
m->tangible_copy_global(123, "gltab", "_G");
|
m->tangible_copy_global(123, "gltab", "_G");
|
||||||
m_ntables = m->number_lua_tables(util::id_vector_create(123));
|
m->number_lua_tables(util::id_vector_create(123));
|
||||||
// LuaAssert(L, m_ntables == 7);
|
|
||||||
LuaAssertStrEq(L, m->numbered_tables_debug_string(),
|
LuaAssertStrEq(L, m->numbered_tables_debug_string(),
|
||||||
"inventory;inventory.cplx;skills;skills.leet;transactions;");
|
"inventory;inventory.cplx;skills;skills.leet;transactions;");
|
||||||
|
|
||||||
@@ -1286,23 +1430,21 @@ LuaDefine(unittests_world, "c") {
|
|||||||
ss->tangible_set_string(123, "skills.leet.TID", "skills.leet");
|
ss->tangible_set_string(123, "skills.leet.TID", "skills.leet");
|
||||||
ss->tangible_set_string(123, "math.TID", "math");
|
ss->tangible_set_string(123, "math.TID", "math");
|
||||||
ss->tangible_set_string(123, "gltab.TID", "gltab");
|
ss->tangible_set_string(123, "gltab.TID", "gltab");
|
||||||
s_ntables = ss->number_lua_tables(util::id_vector_create(123));
|
ss->number_lua_tables(util::id_vector_create(123));
|
||||||
LuaAssert(L, s_ntables == 6);
|
|
||||||
LuaAssertStrEq(L, ss->numbered_tables_debug_string(),
|
LuaAssertStrEq(L, ss->numbered_tables_debug_string(),
|
||||||
"gltab;inventory;math;skills;skills.crap;skills.leet;");
|
"gltab;inventory;math;skills;skills.crap;skills.leet;");
|
||||||
World::TablePairing pairing;
|
ss->pair_lua_tables(util::id_vector_create(123), m->state());
|
||||||
ss->pair_lua_tables(util::id_vector_create(123), m->state(), &pairing);
|
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state()),
|
||||||
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state(), &pairing),
|
|
||||||
"inventory=inventory;skills=skills;skills.leet=skills.leet;");
|
"inventory=inventory;skills=skills;skills.leet=skills.leet;");
|
||||||
|
|
||||||
// Test the creation of new tables during difference transmission.
|
// Test the creation of new tables during difference transmission.
|
||||||
// The master world model above has two tables that couldn't be paired
|
// The master world model above has two tables that couldn't be paired
|
||||||
// to the client: inventory.cplx, and transactions. These two tables
|
// to the client: inventory.cplx, and transactions. These two tables
|
||||||
// should be paired to new, created tables.
|
// should be paired to new, created tables.
|
||||||
ncreate = m->pair_new_tables(&pairing);
|
ncreate = m->pair_new_tables(util::id_vector_create(123), m->state());
|
||||||
LuaAssert(L, ncreate == 2);
|
LuaAssert(L, ncreate == 2);
|
||||||
ss->create_new_tables(ncreate);
|
ss->create_new_tables(ncreate);
|
||||||
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state(), &pairing),
|
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state()),
|
||||||
"inventory=inventory;inventory.cplx=unknown;skills=skills;skills.leet=skills.leet;transactions=unknown;");
|
"inventory=inventory;inventory.cplx=unknown;skills=skills;skills.leet=skills.leet;transactions=unknown;");
|
||||||
|
|
||||||
// Create new clean world models.
|
// Create new clean world models.
|
||||||
|
|||||||
@@ -80,10 +80,6 @@ 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;
|
||||||
|
|
||||||
@@ -123,6 +119,12 @@ public:
|
|||||||
//
|
//
|
||||||
Tangible *tangible_make(lua_State *L, int64_t id, bool pushdb);
|
Tangible *tangible_make(lua_State *L, int64_t id, bool pushdb);
|
||||||
|
|
||||||
|
// Get the tangible ID of the specified LUA tangible database.
|
||||||
|
//
|
||||||
|
// Return zero if the item is not a tangible database.
|
||||||
|
//
|
||||||
|
static int64_t tangible_id(const LuaStack &LS, LuaSlot slot);
|
||||||
|
|
||||||
// Get a pointer to the specified tangible.
|
// Get a pointer to the specified tangible.
|
||||||
//
|
//
|
||||||
// If there's no such tangible, returns nullptr.
|
// If there's no such tangible, returns nullptr.
|
||||||
@@ -135,7 +137,7 @@ public:
|
|||||||
// The value on the lua stack should be a valid lua tangible. If not,
|
// The value on the lua stack should be a valid lua tangible. If not,
|
||||||
// a lua error is generated.
|
// a lua error is generated.
|
||||||
//
|
//
|
||||||
Tangible *tangible_get(lua_State *L, int idx);
|
Tangible *tangible_get(const LuaStack &LS, LuaSlot slot);
|
||||||
|
|
||||||
// Get pointers to many tangibles.
|
// Get pointers to many tangibles.
|
||||||
//
|
//
|
||||||
@@ -240,7 +242,7 @@ public:
|
|||||||
|
|
||||||
// Paired tables debug string. Shows TID=TID pairs, sorted alphabetically.
|
// Paired tables debug string. Shows TID=TID pairs, sorted alphabetically.
|
||||||
//
|
//
|
||||||
std::string paired_tables_debug_string(lua_State *master, const TablePairing *pairing) const;
|
std::string paired_tables_debug_string(lua_State *master) const;
|
||||||
|
|
||||||
// Store a string in the tangible's database.
|
// Store a string in the tangible's database.
|
||||||
//
|
//
|
||||||
@@ -296,21 +298,29 @@ public:
|
|||||||
//
|
//
|
||||||
void unnumber_lua_tables();
|
void unnumber_lua_tables();
|
||||||
|
|
||||||
// Associates numbered tables in the master model with numbered tables in the synch model.
|
// Number tables in the master model to match already-numbered tables in the synch model.
|
||||||
//
|
//
|
||||||
void pair_lua_tables(const IdVector &basis, lua_State *master, TablePairing *pair);
|
void pair_lua_tables(const IdVector &basis, lua_State *master);
|
||||||
|
|
||||||
// Pairs any not-yet-paired master table to a virtually-created table in the synch model.
|
// Pairs every 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.
|
// Returns the number of new tables that need to be created in the synch model.
|
||||||
//
|
//
|
||||||
int pair_new_tables(TablePairing *pair);
|
int pair_new_tables(const IdVector &basis, lua_State *master);
|
||||||
|
|
||||||
// This is followup for pair_new_tables: actually create the new tables that
|
// This is followup for pair_new_tables: actually create the new tables that
|
||||||
// were virtually created in the pair_new_tables step.
|
// were virtually created in the pair_new_tables step.
|
||||||
//
|
//
|
||||||
void create_new_tables(int n);
|
void create_new_tables(int n);
|
||||||
|
|
||||||
|
// Compare the general tables.
|
||||||
|
//
|
||||||
|
void diff_lua_tables(lua_State *master, StreamBuffer *sb);
|
||||||
|
|
||||||
|
// Compare the tangible databases.
|
||||||
|
//
|
||||||
|
void diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Type of model
|
// Type of model
|
||||||
util::WorldType world_type_;
|
util::WorldType world_type_;
|
||||||
|
|||||||
@@ -6,5 +6,6 @@
|
|||||||
inspect.lua
|
inspect.lua
|
||||||
ut-table.lua
|
ut-table.lua
|
||||||
ut-globaldb.lua
|
ut-globaldb.lua
|
||||||
|
ut-tablecmp.lua
|
||||||
player.lua
|
player.lua
|
||||||
login.lua
|
login.lua
|
||||||
|
|||||||
71
luprex/core/lua/ut-tablecmp.lua
Normal file
71
luprex/core/lua/ut-tablecmp.lua
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
local tdc = table.diffcompare
|
||||||
|
|
||||||
|
function unittests.tablecmp()
|
||||||
|
-- No differences in these simple-valued tables.
|
||||||
|
assert(tdc(nil, {a=true}, nil, {a=true}) == "")
|
||||||
|
assert(tdc(nil, {a=5}, nil, {a=5}) == "");
|
||||||
|
assert(tdc(nil, {a="foo"}, nil, {a="foo"}) == "")
|
||||||
|
|
||||||
|
-- Test transmission of missing simple values.
|
||||||
|
assert(tdc(nil, {a=true}, nil, {}) == "a=true;")
|
||||||
|
assert(tdc(nil, {a=5}, nil, {}) == "a=5;");
|
||||||
|
assert(tdc(nil, {a="foo"}, nil, {}) == "a=foo;")
|
||||||
|
|
||||||
|
-- Test the replacement of simple values.
|
||||||
|
assert(tdc(nil, {a=true}, nil, {a=false}) == "a=true;")
|
||||||
|
assert(tdc(nil, {a=5}, nil, {a=4}) == "a=5;");
|
||||||
|
assert(tdc(nil, {a="foo"}, nil, {a="bar"}) == "a=foo;")
|
||||||
|
|
||||||
|
-- Test the clearing of values.
|
||||||
|
assert(tdc(nil, {}, nil, {a=true}) == "a=nil;")
|
||||||
|
assert(tdc(nil, {}, nil, {a=5}) == "a=nil;");
|
||||||
|
assert(tdc(nil, {}, nil, {a="foo"}) == "a=nil;")
|
||||||
|
|
||||||
|
-- Try boolean keys.
|
||||||
|
assert(tdc(nil, {[true]=3}, nil, {}) == "true=3;")
|
||||||
|
assert(tdc(nil, {}, nil, {[true]=3}) == "true=nil;")
|
||||||
|
|
||||||
|
-- Try number keys.
|
||||||
|
assert(tdc(nil, {[7]=3}, nil, {}) == "7=3;")
|
||||||
|
assert(tdc(nil, {}, nil, {[7]=3}) == "7=nil;")
|
||||||
|
|
||||||
|
-- Try a table with multiple keys.
|
||||||
|
assert(tdc(nil, {a=4, b=5, c=6}, nil, {b=5, c=7, d=8}) == "a=4;c=6;d=nil;")
|
||||||
|
|
||||||
|
-- Nonsortable keys should be ignored (no diffs).
|
||||||
|
assert(tdc(nil, {[{}]=3}, nil, {}) == "")
|
||||||
|
|
||||||
|
-- Try a table containing a class.
|
||||||
|
assert(tdc(nil, {a=deque}, nil, {}) == "a=class deque;")
|
||||||
|
|
||||||
|
-- Try a table containing a pointer to the global environment.
|
||||||
|
assert(tdc(nil, {a=_G}, nil, {}) == "a=globals;")
|
||||||
|
|
||||||
|
-- GlobalDB tables should be forced to NIL.
|
||||||
|
assert(tdc(nil, {a=global("foo")}, nil, {a=global("foo")}) == "a=nil;");
|
||||||
|
assert(tdc(nil, {}, nil, {a=global("foo")}) == "a=nil;");
|
||||||
|
assert(tdc(nil, {a=global("foo")}, nil, {}) == "");
|
||||||
|
|
||||||
|
-- Set up some numbered tables for tests involving such.
|
||||||
|
local mtab10 = {}
|
||||||
|
local stab10 = {}
|
||||||
|
local mtab11 = {}
|
||||||
|
local stab11 = {}
|
||||||
|
local mtnmap = {}
|
||||||
|
local stnmap = {}
|
||||||
|
mtnmap[mtab10] = 10
|
||||||
|
stnmap[stab10] = 10
|
||||||
|
mtnmap[mtab11] = 11
|
||||||
|
stnmap[stab11] = 11
|
||||||
|
|
||||||
|
-- confirm that numbered tables are being transmitted.
|
||||||
|
assert(tdc(mtnmap, {a=mtab10}, stnmap, {}) == "a=table 10;")
|
||||||
|
assert(tdc(mtnmap, {a=mtab10}, stnmap, {a=stab10}) == "")
|
||||||
|
assert(tdc(mtnmap, {a=3}, stnmap, {a=stab10}) == "a=3;")
|
||||||
|
assert(tdc(mtnmap, {}, stnmap, {a=stab10}) == "a=nil;")
|
||||||
|
|
||||||
|
-- we're not going to test tangibles
|
||||||
|
-- creating tangibles here is too difficult.
|
||||||
|
end
|
||||||
|
|
||||||
Reference in New Issue
Block a user