Working on diff xmission
This commit is contained in:
@@ -13,6 +13,7 @@ CPP_FILES=\
|
||||
cpp/globaldb.cpp\
|
||||
cpp/sched.cpp\
|
||||
cpp/table.cpp\
|
||||
cpp/tablecmp.cpp\
|
||||
cpp/gui.cpp\
|
||||
cpp/luasnap.cpp\
|
||||
cpp/animqueue.cpp\
|
||||
|
||||
@@ -45,6 +45,6 @@ LuaDefine(globaldb_global, "f") {
|
||||
LS.newtable(globaltab);
|
||||
LS.rawset(globaldb, globalname, globaltab);
|
||||
LS.rawset(globaltab, "__global", globalname);
|
||||
LS.settabletype(globaltab, LuaStack::TAB_GLOBALDB);
|
||||
LS.settabletype(globaltab, LUA_TT_GLOBALDB);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "luastack.hpp"
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
LuaSpecial LuaRegistry(LUA_REGISTRYINDEX);
|
||||
LuaNilMarker LuaNil;
|
||||
@@ -234,7 +235,7 @@ void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const {
|
||||
}
|
||||
|
||||
// Repair the special fields.
|
||||
LS.settabletype(classtab, TAB_CLASS);
|
||||
LS.settabletype(classtab, LUA_TT_CLASS);
|
||||
LS.rawset(classtab, "__index", classtab);
|
||||
LS.rawset(classtab, "__class", classname);
|
||||
|
||||
@@ -242,23 +243,48 @@ void LuaStack::makeclass(LuaSlot classtab, LuaSlot classname) const {
|
||||
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);
|
||||
switch (type) {
|
||||
case LUA_TBOOLEAN:
|
||||
lua_pushboolean(L, lua_toboolean(L_, key));
|
||||
lua_pushboolean(otherstack.L_, lua_toboolean(L_, key));
|
||||
lua_replace(otherstack.L_, otherslot);
|
||||
break;
|
||||
case LUA_TNUMBER:
|
||||
lua_pushnumber(L, lua_tonumber(L_, key));
|
||||
lua_pushnumber(otherstack.L_, lua_tonumber(L_, key));
|
||||
lua_replace(otherstack.L_, otherslot);
|
||||
break;
|
||||
case LUA_TSTRING: {
|
||||
size_t 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;
|
||||
}
|
||||
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());
|
||||
return TableType(bits & 0x000F);
|
||||
return LUA_TT_GENERAL + (bits & 0x000F);
|
||||
}
|
||||
|
||||
void LuaStack::settabletype(LuaSlot tab, TableType t) {
|
||||
lua_modflagbits(L_, tab.index(), 0x000F, t);
|
||||
void LuaStack::settabletype(LuaSlot tab, int t) const {
|
||||
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());
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -215,6 +215,18 @@ using LuaTypeTag = lua_CFunction;
|
||||
template<typename T>
|
||||
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 {
|
||||
private:
|
||||
int narg_;
|
||||
@@ -360,6 +372,8 @@ public:
|
||||
int result();
|
||||
|
||||
public:
|
||||
lua_State *state() const { return L_; }
|
||||
|
||||
int type(LuaSlot s) const { return lua_type(L_, s); }
|
||||
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;
|
||||
|
||||
void makeclass(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 {
|
||||
return lua_rawequal(L_, v1, v2);
|
||||
@@ -475,21 +485,16 @@ public:
|
||||
}
|
||||
|
||||
// Lua flagbits manipulation: Table types.
|
||||
enum TableType {
|
||||
TAB_GENERAL, // A general-purpose table.
|
||||
TAB_REGISTRY, // The registry table.
|
||||
TAB_GLOBALENV, // The global environment table.
|
||||
TAB_TANGIBLE, // A tangible's database.
|
||||
TAB_TANGIBLEMETA, // A tangible's metatable.
|
||||
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);
|
||||
int gettabletype(LuaSlot tab) const;
|
||||
void settabletype(LuaSlot tab, int t) const;
|
||||
|
||||
// If slot is a table, returns the LUA_TT_XXX table type.
|
||||
// If slot is not a table, returns the LUA_TXXX general type.
|
||||
int xtype(LuaSlot slot) const;
|
||||
|
||||
// Lua flagbits manipulation: visited bit.
|
||||
bool getvisited(LuaSlot tab);
|
||||
void setvisited(LuaSlot tab, bool visited);
|
||||
bool getvisited(LuaSlot tab) const;
|
||||
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 "gui.hpp"
|
||||
#include "traceback.hpp"
|
||||
#include "tablecmp.hpp"
|
||||
#include <iostream>
|
||||
|
||||
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);
|
||||
|
||||
// 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.
|
||||
LS.getglobaltable(globtab);
|
||||
LS.settabletype(globtab, LuaStack::TAB_GLOBALENV);
|
||||
LS.settabletype(globtab, LUA_TT_GLOBALENV);
|
||||
|
||||
// Create the tangibles table in the registry.
|
||||
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
||||
@@ -114,25 +115,34 @@ World::TanVector World::tangible_get_all(const IdVector &ids) const {
|
||||
return result;
|
||||
}
|
||||
|
||||
Tangible *World::tangible_get(lua_State *L, int idx) {
|
||||
Tangible *result = nullptr;
|
||||
int top = lua_gettop(L);
|
||||
if (lua_istable(L, idx)) {
|
||||
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));
|
||||
}
|
||||
Tangible *World::tangible_get(const LuaStack &LS, LuaSlot tab) {
|
||||
int64_t id = tangible_id(LS, tab);
|
||||
if (id == 0) {
|
||||
luaL_error(LS.state(), "parameter is not a tangible");
|
||||
}
|
||||
lua_settop(L, top);
|
||||
Tangible *result = tangible_get(id);
|
||||
if (result == nullptr) {
|
||||
luaL_error(L, "parameter is not a tangible");
|
||||
luaL_error(LS.state(), "parameter is not a tangible");
|
||||
}
|
||||
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) {
|
||||
lua_State *L = state();
|
||||
LuaVar tangibles, database;
|
||||
@@ -153,6 +163,7 @@ void World::tangible_delete(int64_t id) {
|
||||
// Clear out the database.
|
||||
LS.clearmetatable(database);
|
||||
LS.cleartable(database);
|
||||
LS.settabletype(database, LUA_TT_DEADTANGIBLE);
|
||||
|
||||
// Remove the lua portion from the tangibles table.
|
||||
LS.rawset(tangibles, id, LuaNil);
|
||||
@@ -221,7 +232,7 @@ std::string World::numbered_tables_debug_string() const {
|
||||
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();
|
||||
LuaVar mntmap, sntmap, mtab, stab, mtid, stid;
|
||||
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.
|
||||
MLS.rawget(mntmap, 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++) {
|
||||
if (pairing->mpair[i] == 0) continue;
|
||||
for (int i = 1; i <= m_ntables; i++) {
|
||||
MLS.rawget(mtab, mntmap, i);
|
||||
SLS.rawget(stab, sntmap, pairing->mpair[i]);
|
||||
std::string mname = "unknown";
|
||||
std::string sname = "unknown";
|
||||
if (MLS.istable(mtab)) {
|
||||
SLS.rawget(stab, sntmap, i);
|
||||
if (MLS.istable(mtab) && SLS.istable(stab)) {
|
||||
std::string mname = "unknown";
|
||||
std::string sname = "unknown";
|
||||
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));
|
||||
}
|
||||
result.push_back(std::make_pair(mname, sname));
|
||||
}
|
||||
MLS.result();
|
||||
SLS.result();
|
||||
@@ -376,8 +387,8 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) {
|
||||
LS.setmetatable(database, metatab);
|
||||
|
||||
// Mark the tangible using the tabletype field.
|
||||
LS.settabletype(database, LuaStack::TAB_TANGIBLE);
|
||||
LS.settabletype(metatab, LuaStack::TAB_TANGIBLEMETA);
|
||||
LS.settabletype(database, LUA_TT_TANGIBLE);
|
||||
LS.settabletype(metatab, LUA_TT_TANGIBLEMETA);
|
||||
|
||||
// Store the database into the tangibles table.
|
||||
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.
|
||||
// Meanwhile, the client will number tables in the client-synchronous model.
|
||||
master->number_lua_tables(closetans);
|
||||
int s_ntables = number_lua_tables(closetans);
|
||||
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);
|
||||
pair_lua_tables(closetans, master->state());
|
||||
int ncreate = pair_new_tables(closetans, master->state());
|
||||
sb->write_int32(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 m_closehash = sb->read_hashvalue();
|
||||
assert(closehash == m_closehash);
|
||||
int s_ntables = number_lua_tables(closetans);
|
||||
assert(s_ntables == sb->read_int32());
|
||||
number_lua_tables(closetans);
|
||||
int ncreate = sb->read_int32();
|
||||
create_new_tables(ncreate);
|
||||
}
|
||||
@@ -890,19 +898,12 @@ int World::number_lua_tables(const IdVector &basis) {
|
||||
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) {
|
||||
if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) {
|
||||
lua_checkstack(L, 10);
|
||||
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
|
||||
// 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)) {
|
||||
@@ -919,22 +919,16 @@ int World::number_lua_tables(const IdVector &basis) {
|
||||
LS.rawset(ntmap, id, tab);
|
||||
// Traverse the metatable.
|
||||
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_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) {
|
||||
if (LS.istable(val) && LS.gettabletype(val)==LUA_TT_GENERAL) {
|
||||
lua_checkstack(L, 10);
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
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);
|
||||
|
||||
// Fetch the tangible databases
|
||||
SLS.rawget(stangibles, LuaRegistry, "tangibles");
|
||||
MLS.rawget(mtangibles, LuaRegistry, "tangibles");
|
||||
|
||||
// Fetch the synchronous model tnmap and ntmap
|
||||
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);
|
||||
|
||||
// Initialize the master model tnmap and ntmap
|
||||
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);
|
||||
|
||||
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)) {
|
||||
if (!MLS.issortablekey(mkey)) continue;
|
||||
if (!MLS.istable(mval)) continue;
|
||||
MLS.movesortablekey(mkey, synch);
|
||||
lua_replace(synch, skey.index());
|
||||
MLS.movesortablekey(mkey, SLS, skey);
|
||||
SLS.rawget(sval, stab, skey);
|
||||
if (!SLS.istable(sval)) continue;
|
||||
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) {
|
||||
lua_replace(master, mtab.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);
|
||||
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);
|
||||
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.
|
||||
int idx = SLS.ckinteger(sidx);
|
||||
assert((idx >= 1) && (idx <= s_ntables));
|
||||
// Pair the tables.
|
||||
MLS.rawset(mtnmap, mtab, idx);
|
||||
MLS.rawset(mntmap, idx, mtab);
|
||||
paired[idx] = true;
|
||||
// Potentially pair the metatables.
|
||||
MLS.getmetatable(mval, mtab);
|
||||
if (MLS.istable(mval)) {
|
||||
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)) {
|
||||
if (!MLS.issortablekey(mkey)) continue;
|
||||
if (!MLS.istable(mval)) continue;
|
||||
MLS.movesortablekey(mkey, synch);
|
||||
lua_replace(synch, skey.index());
|
||||
MLS.movesortablekey(mkey, SLS, skey);
|
||||
SLS.rawget(sval, stab, skey);
|
||||
if (!SLS.istable(sval)) continue;
|
||||
lua_checkstack(master, 20);
|
||||
@@ -1034,16 +1045,71 @@ void World::pair_lua_tables(const IdVector &basis, lua_State *master, TablePairi
|
||||
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);
|
||||
int World::pair_new_tables(const IdVector &basis, lua_State *master) {
|
||||
// This is conceptually recursive, but we're going to use an
|
||||
// explicit stack (the lua stack).
|
||||
lua_State *L = master;
|
||||
LuaVar tnmap, ntmap, tangibles, tab, key, val, xid;
|
||||
LuaStack LS(L, tnmap, ntmap, tangibles, tab, key, val, xid);
|
||||
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) {
|
||||
@@ -1065,12 +1131,87 @@ void World::create_new_tables(int n) {
|
||||
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") {
|
||||
LuaArg tanobj;
|
||||
LuaRet graphic, plane, x, y, z, facing;
|
||||
LuaStack LS(L, tanobj, graphic, plane, x, y, z, facing);
|
||||
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();
|
||||
LS.set(graphic, aqback.graphic());
|
||||
LS.set(plane, aqback.plane());
|
||||
@@ -1085,7 +1226,7 @@ LuaDefine(tangible_animate, "c") {
|
||||
LuaArg tanobj, config;
|
||||
LuaStack LS(L, tanobj, config);
|
||||
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);
|
||||
const AnimStep &prev = tan->anim_queue_.back();
|
||||
AnimStep step;
|
||||
@@ -1103,7 +1244,7 @@ LuaDefine(tangible_setclass, "c") {
|
||||
LuaVar classtab, mt;
|
||||
LuaStack LS(L, tanobj, classname, classtab, mt);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
w->tangible_get(L, tanobj.index());
|
||||
w->tangible_get(LS, tanobj);
|
||||
LS.getclass(classtab, classname);
|
||||
LS.getmetatable(mt, tanobj);
|
||||
LS.rawset(mt, "__index", classtab);
|
||||
@@ -1114,7 +1255,7 @@ LuaDefine(tangible_delete, "c") {
|
||||
LuaArg tanobj;
|
||||
LuaStack LS(L, tanobj);
|
||||
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.
|
||||
if (tan->is_an_actor()) {
|
||||
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);
|
||||
World *w = World::fetch_global_pointer(L);
|
||||
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()) {
|
||||
luaL_error(L, "redirect source is not an actor");
|
||||
}
|
||||
if (LS.isnil(actor2)) {
|
||||
w->redirects_[tan1->id()] = 0;
|
||||
} else {
|
||||
Tangible *tan2 = w->tangible_get(L, actor2.index());
|
||||
Tangible *tan2 = w->tangible_get(LS, actor2);
|
||||
tan2->configure_id_pool_for_actor();
|
||||
w->redirects_[tan1->id()] = tan2->id();
|
||||
}
|
||||
@@ -1205,6 +1346,14 @@ LuaDefine(tangible_redirect, "c") {
|
||||
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") {
|
||||
if ((lua_gettop(L) != 1) || (lua_type(L, -1) != LUA_TNUMBER)) {
|
||||
luaL_error(L, "Argument to wait must be a number.");
|
||||
@@ -1217,16 +1366,12 @@ LuaDefine(world_getregistry, "f") {
|
||||
return 1;
|
||||
}
|
||||
|
||||
LuaDefine(world_gettabletype, "f") {
|
||||
LuaDefine(world_xtype, "f") {
|
||||
LuaArg tab;
|
||||
LuaRet ttype;
|
||||
LuaStack LS(L, tab, ttype);
|
||||
if (LS.istable(tab)) {
|
||||
LuaStack::TableType tt = LS.gettabletype(tab);
|
||||
LS.set(ttype, int(tt));
|
||||
} else {
|
||||
luaL_error(L, "Not a table");
|
||||
}
|
||||
LuaRet rtype;
|
||||
LuaStack LS(L, tab, rtype);
|
||||
int xt = LS.xtype(tab);
|
||||
LS.set(rtype, xt);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
@@ -1237,10 +1382,10 @@ LuaDefine(world_settabletype, "f") {
|
||||
luaL_error(L, "Not a table");
|
||||
}
|
||||
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");
|
||||
}
|
||||
LS.settabletype(tab, LuaStack::TableType(tt));
|
||||
LS.settabletype(tab, tt);
|
||||
return LS.result();
|
||||
}
|
||||
|
||||
@@ -1255,7 +1400,7 @@ static bool worlds_identical(const std::unique_ptr<World> &w1, const std::unique
|
||||
LuaDefine(unittests_world, "c") {
|
||||
std::unique_ptr<World> m, ss, cs;
|
||||
StreamBuffer sb;
|
||||
int m_ntables, s_ntables, ncreate;
|
||||
int ncreate;
|
||||
|
||||
// Test the numbering of lua tables. We create some general
|
||||
// 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_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);
|
||||
m->number_lua_tables(util::id_vector_create(123));
|
||||
LuaAssertStrEq(L, m->numbered_tables_debug_string(),
|
||||
"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, "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);
|
||||
ss->number_lua_tables(util::id_vector_create(123));
|
||||
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),
|
||||
ss->pair_lua_tables(util::id_vector_create(123), m->state());
|
||||
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state()),
|
||||
"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);
|
||||
ncreate = m->pair_new_tables(util::id_vector_create(123), m->state());
|
||||
LuaAssert(L, ncreate == 2);
|
||||
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;");
|
||||
|
||||
// Create new clean world models.
|
||||
|
||||
@@ -80,10 +80,6 @@ public:
|
||||
using IdVector = util::IdVector;
|
||||
using TanVector = std::vector<const Tangible*>;
|
||||
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 RadiusClose = 10.0;
|
||||
|
||||
@@ -123,6 +119,12 @@ public:
|
||||
//
|
||||
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.
|
||||
//
|
||||
// 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,
|
||||
// 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.
|
||||
//
|
||||
@@ -240,7 +242,7 @@ public:
|
||||
|
||||
// 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.
|
||||
//
|
||||
@@ -296,21 +298,29 @@ public:
|
||||
//
|
||||
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.
|
||||
//
|
||||
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
|
||||
// were virtually created in the pair_new_tables step.
|
||||
//
|
||||
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:
|
||||
// Type of model
|
||||
util::WorldType world_type_;
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
inspect.lua
|
||||
ut-table.lua
|
||||
ut-globaldb.lua
|
||||
ut-tablecmp.lua
|
||||
player.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