Worked on code for difference transmission

This commit is contained in:
2021-09-09 18:23:17 -04:00
parent bac1a7b876
commit aedd8ca43a
8 changed files with 456 additions and 403 deletions

View File

@@ -200,7 +200,11 @@ static void pprint_r(Inspector &insp, int level, LuaSlot root) {
pprint_r(insp, level + 1, key);
(*insp.stream) << "]";
}
(*insp.stream) << " = ";
if (insp.indent) {
(*insp.stream) << " = ";
} else {
(*insp.stream) << "=";
}
pprint_r(insp, level + 1, val);
}
}

View File

@@ -37,7 +37,7 @@ bool string_quote(LuaStack &LS, LuaSlot val, std::ostream *os);
// Maxlen specifies the maximum number of characters output. If
// this is exceeded, then the printout is truncated.
//
void pprint(LuaStack &LS, LuaSlot val, int indent, int maxlen, std::ostream *os);
void pprint(LuaStack &LS, LuaSlot val, bool indent, std::ostream *os);
// The following lua interfaces to this code are included:
//

View File

@@ -36,13 +36,13 @@ bool is_identifier(const std::string &str) {
}
void quote_string(const std::string &s, std::ostream *os) {
bool usesinglequote = false;
bool anysq = false;
bool anydq = false;
for (char c : s) {
if (c == '"') {
usesinglequote = true;
break;
}
if (c == '\'') anysq = true;
if (c == '"') anydq = true;
}
bool usesinglequote = (!anysq)||(anydq);
(*os) << (usesinglequote ? '\'' : '"');
for (char c : s) {
if (c >= 32) {

View File

@@ -0,0 +1,186 @@
#include "world.hpp"
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(LS, tanobj);
const AnimStep &aqback = tan->anim_queue_.back();
LS.set(graphic, aqback.graphic());
LS.set(plane, aqback.plane());
LS.set(x, aqback.xyz().x);
LS.set(y, aqback.xyz().y);
LS.set(z, aqback.xyz().z);
LS.set(facing, aqback.facing());
return LS.result();
}
LuaDefine(tangible_animate, "c") {
LuaArg tanobj, config;
LuaStack LS(L, tanobj, config);
World *w = World::fetch_global_pointer(L);
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;
step.from_lua(L, config.index(), prev);
if (step.action() == "") {
luaL_error(L, "animation action must be specified");
}
tan->anim_queue_.add(id, step);
tan->update_plane_item();
return LS.result();
}
LuaDefine(tangible_setclass, "c") {
LuaArg tanobj, classname;
LuaVar classtab, mt;
LuaStack LS(L, tanobj, classname, classtab, mt);
World *w = World::fetch_global_pointer(L);
w->tangible_get(LS, tanobj);
LS.getclass(classtab, classname);
LS.getmetatable(mt, tanobj);
LS.rawset(mt, "__index", classtab);
return LS.result();
}
LuaDefine(tangible_delete, "c") {
LuaArg tanobj;
LuaStack LS(L, tanobj);
World *w = World::fetch_global_pointer(L);
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.");
return 0;
}
w->tangible_delete(tan->id());
return LS.result();
}
LuaDefine(tangible_build, "c") {
LuaArg config;
LuaVar classname, classtab, mt;
LuaRet database;
LuaStack LS(L, config, classname, classtab, database, mt);
LS.checktable(config);
// Get the class of the new tangible.
LS.rawget(classname, config, "class");
if (LS.isnil(classname)) {
luaL_error(L, "must specify a class name");
}
LS.getclass(classtab, classname);
// Parse the initial animation step.
AnimStep initstep, blank;
initstep.from_lua(L, config.index(), blank);
if (!initstep.has_xyz()) {
luaL_error(L, "You must specify (X,Y,Z) for new tangible");
}
if (!initstep.has_plane()) {
luaL_error(L, "You must specify plane for new tangible");
}
if (!initstep.has_graphic()) {
luaL_error(L, "You must specify graphic for new tangible");
}
// TODO: generate error if there's extra crap in the config table.
World *w = World::fetch_global_pointer(L);
Tangible *tan = w->tangible_make(L, 0, true);
lua_replace(L, database.index());
// Update the class of the new tangible.
LS.getmetatable(mt, database);
LS.rawset(mt, "__index", classtab);
// Update the animation queue and planemap of the new tangible.
int64_t stepid = w->id_global_pool_.alloc_id_for_thread(L);
tan->anim_queue_.add(stepid, initstep);
tan->update_plane_item();
return LS.result();
}
LuaDefine(tangible_get, "c") {
LuaArg id;
LuaVar tangibles;
LuaRet database;
LuaStack LS(L, id, tangibles, database);
int64_t nid = LS.ckinteger(id);
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(database, tangibles, id);
if (!LS.istable(database)) {
luaL_error(L, "Not a tangible ID: %d", nid);
}
return LS.result();
}
LuaDefine(tangible_redirect, "c") {
LuaArg actor1, actor2, bldz;
LuaStack LS(L, actor1, actor2, bldz);
World *w = World::fetch_global_pointer(L);
bool bulldoze = LS.ckboolean(bldz);
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(LS, actor2);
tan2->configure_id_pool_for_actor();
w->redirects_[tan1->id()] = tan2->id();
}
if (bulldoze) {
w->tangible_delete(tan1->id());
}
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.");
}
return lua_yield(L, 1);
}
LuaDefine(world_getregistry, "f") {
lua_pushvalue(L, LUA_REGISTRYINDEX);
return 1;
}
LuaDefine(world_xtype, "f") {
LuaArg tab;
LuaRet rtype;
LuaStack LS(L, tab, rtype);
int xt = LS.xtype(tab);
LS.set(rtype, xt);
return LS.result();
}
LuaDefine(world_settabletype, "f") {
LuaArg tab, ttype;
LuaStack LS(L, tab, ttype);
if (!LS.istable(tab)) {
luaL_error(L, "Not a table");
}
int tt = LS.ckinteger(ttype);
if ((tt < LUA_TT_GENERAL) || (tt > LUA_TT_CLASS)) {
luaL_error(L, "table type out of range");
}
LS.settabletype(tab, tt);
return LS.result();
}

View File

@@ -341,6 +341,8 @@ void World::diff_numbered_tables(lua_State *master, StreamBuffer *sb) {
assert(lua_gettop(master) == m_top);
}
sb->overwrite_int32(write_count_after, nmodified);
SLS.result();
MLS.result();
}
@@ -373,6 +375,8 @@ void World::diff_tangible_databases(const IdVector &basis, lua_State *master, St
assert(lua_gettop(master) == m_top);
}
sb->overwrite_int32(write_count_after, nmodified);
SLS.result();
MLS.result();
}
void World::patch_numbered_tables(StreamBuffer *sb) {

View File

@@ -4,6 +4,7 @@
#include "animqueue.hpp"
#include "gui.hpp"
#include "traceback.hpp"
#include "print.hpp"
#include <iostream>
void World::store_global_pointer(lua_State *L, World *v) {
@@ -145,6 +146,49 @@ int64_t World::tangible_id(const LuaStack &LS, LuaSlot tab) {
return id;
}
Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) {
// Get a state if we don't already have one.
if (L == nullptr) {
L = state();
assert(!pushdb);
}
LuaVar tangibles, metatab;
LuaRet database;
LuaStack LS(L, tangibles, database, metatab);
// Allocate an ID if we don't already have one.
if (id == 0) id = id_global_pool_.alloc_id_for_thread(L);
// Create the C++ part of the structure.
std::unique_ptr<Tangible> &t = tangibles_[id];
assert (t == nullptr);
t.reset(new Tangible(this, id));
// Create the tangible's database and metatable.
LS.set(database, LuaNewTable);
LS.set(metatab, LuaNewTable);
LS.setmetatable(database, metatab);
// Mark the tangible using the tabletype field.
LS.settabletype(database, LUA_TT_TANGIBLE);
LS.settabletype(metatab, LUA_TT_TANGIBLEMETA);
// Store the database into the tangibles table.
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawset(tangibles, id, database);
// Populate the database and metatable with initial stuff.
LS.rawset(database, "inventory", LuaNewTable);
LS.rawset(metatab, "id", id);
LS.rawset(metatab, "threads", LuaNewTable);
// LS.rawset(metatab, "__metatable", LuaNil);
LS.result();
if (!pushdb) lua_pop(L, 1);
return t.get();
}
void World::tangible_delete(int64_t id) {
lua_State *L = state();
LuaVar tangibles, database;
@@ -204,6 +248,25 @@ std::string World::tangible_ids_debug_string() const {
return util::id_vector_debug_string(idv);
}
std::string World::tangible_pprint(int64_t id) const {
lua_State *L = state();
LuaVar tangibles, tan, meta;
LuaStack LS(L, tangibles, tan, meta);
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawgeti(tan, tangibles, id);
std::ostringstream oss;
if (LS.istable(tan)) {
LS.getmetatable(meta, tan);
LS.clearmetatable(tan);
pprint(LS, tan, false, &oss);
LS.setmetatable(tan, meta);
} else {
oss << "<no such tangible: " << id << ">";
}
LS.result();
return oss.str();
}
std::string World::numbered_tables_debug_string() const {
lua_State *L = state();
LuaVar ntmap, tab, tid;
@@ -364,49 +427,6 @@ util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_now
return v;
}
Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) {
// Get a state if we don't already have one.
if (L == nullptr) {
L = state();
assert(!pushdb);
}
LuaVar tangibles, metatab;
LuaRet database;
LuaStack LS(L, tangibles, database, metatab);
// Allocate an ID if we don't already have one.
if (id == 0) id = id_global_pool_.alloc_id_for_thread(L);
// Create the C++ part of the structure.
std::unique_ptr<Tangible> &t = tangibles_[id];
assert (t == nullptr);
t.reset(new Tangible(this, id));
// Create the tangible's database and metatable.
LS.set(database, LuaNewTable);
LS.set(metatab, LuaNewTable);
LS.setmetatable(database, metatab);
// Mark the tangible using the tabletype field.
LS.settabletype(database, LUA_TT_TANGIBLE);
LS.settabletype(metatab, LUA_TT_TANGIBLEMETA);
// Store the database into the tangibles table.
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawset(tangibles, id, database);
// Populate the database and metatable with initial stuff.
LS.rawset(database, "inventory", LuaNewTable);
LS.rawset(metatab, "id", id);
LS.rawset(metatab, "threads", LuaNewTable);
// LS.rawset(metatab, "__metatable", LuaNil);
LS.result();
if (!pushdb) lua_pop(L, 1);
return t.get();
}
World::Redirects World::fetch_redirects() {
World::Redirects result = std::move(redirects_);
redirects_.clear();
@@ -701,94 +721,13 @@ void World::rollback() {
deserialize(&snapshot_);
}
void World::difference_transmit(int64_t actor_id, World *master, StreamBuffer *sb) {
StreamBuffer tsb;
// Get the actor in both models. s_actor may be nil.
const Tangible *s_actor = tangible_get(actor_id);
const Tangible *m_actor = master->tangible_get(actor_id);
assert(m_actor != nullptr);
// Pass one: update the actor essentials. Create actor if doesn't exist.
assert(tsb.at_eof());
diff_actor_essentials(m_actor, s_actor, &tsb);
tsb.copy_into(sb);
patch_actor_essentials(&tsb);
// Get the list of tangibles visible in either model.
// Some tangibles may be missing in the master, some may be missing in the sync.
util::IdVector visible = util::sort_union_id_vectors(
util::IdVector World::get_visible_union(int64_t actor_id, World *master) {
return util::sort_union_id_vectors(
master->get_near_unsorted(actor_id, RadiusVisibility, true),
get_near_unsorted(actor_id, RadiusVisibility, true));
TanVector m_visible = master->tangible_get_all(visible);
TanVector s_visible = tangible_get_all(visible);
assert(m_visible.size() == s_visible.size());
// Pass two: update visible animations.
assert(tsb.at_eof());
diff_visible_animations(m_visible, s_visible, &tsb);
tsb.copy_into(sb);
patch_visible_animations(&tsb);
// Copy the version number from master animqueue to synch animqueue.
for (int i = 0; i < int(m_visible.size()); i++) {
const Tangible *m_tan = m_visible[i];
if (m_tan != nullptr) {
Tangible *s_tan = const_cast<Tangible *>(s_visible[i]);
if (s_tan == nullptr) {
s_tan = tangible_get(m_tan->id());
assert(s_tan != nullptr);
}
s_tan->anim_queue_.update_version(m_tan->anim_queue_);
}
}
// Obtain the list of tangibles whose tables we're going to transmit.
util::IdVector closetans = get_near(actor_id, RadiusClose, true);
assert(closetans == master->get_near(actor_id, RadiusClose, true));
util::HashValue closehash = util::hash_id_vector(closetans);
// Confirm the close tangibles with the client.
sb->write_hashvalue(closehash);
// Number tables in both the master and the synchronous model.
// Meanwhile, the client will number tables in the client-synchronous model.
master->number_lua_tables(closetans);
number_lua_tables(closetans);
// Pair tables from the synchronous and master models.
pair_lua_tables(closetans, master->state());
int ncreate = number_remaining_tables(closetans, master->state());
sb->write_int32(ncreate);
create_new_tables(ncreate);
assert(tsb.at_eof());
}
void World::apply_differences(StreamBuffer *sb) {
int64_t actor_id = patch_actor_essentials(sb);
patch_visible_animations(sb);
util::IdVector closetans = get_near(actor_id, RadiusClose, true);
util::HashValue closehash = util::hash_id_vector(closetans);
util::HashValue m_closehash = sb->read_hashvalue();
assert(closehash == m_closehash);
number_lua_tables(closetans);
int ncreate = sb->read_int32();
create_new_tables(ncreate);
}
void World::diff_actor_essentials(const Tangible *m_actor, const Tangible *s_actor, StreamBuffer *sb) {
sb->write_int64(m_actor->id());
if (s_actor == nullptr) {
m_actor->id_player_pool_.serialize(sb);
m_actor->anim_queue_.serialize(sb);
} else {
s_actor->id_player_pool_.diff(m_actor->id_player_pool_, sb);
s_actor->anim_queue_.diff(m_actor->anim_queue_, sb);
}
}
int64_t World::patch_actor_essentials(StreamBuffer *sb) {
int64_t World::patch_actor(StreamBuffer *sb) {
int64_t actor_id = sb->read_int64();
Tangible *s_actor = tangible_get(actor_id);
if (s_actor == nullptr) {
@@ -803,58 +742,31 @@ int64_t World::patch_actor_essentials(StreamBuffer *sb) {
return actor_id;
}
void World::diff_visible_animations(const TanVector &mvis, const TanVector &svis, StreamBuffer *sb) {
// For each tangible missing in the synchronous model, send the
// necessary information to create the tangible.
sb->write_int32(0);
int count_pos = sb->total_writes();
int count = 0;
for (int i = 0; i < int(svis.size()); i++) {
const Tangible *mt = mvis[i];
const Tangible *st = svis[i];
if (st == nullptr) {
count += 1;
sb->write_int64(mt->id());
mt->anim_queue_.serialize(sb);
}
}
sb->overwrite_int32(count_pos, count);
void World::diff_actor(int64_t actor_id, World *master, StreamBuffer *xsb) {
StreamBuffer tsb;
// For each tangible present in the synchronous model that doesn't
// exist in the master model, send command to delete it.
sb->write_int32(0);
count_pos = sb->total_writes();
count = 0;
for (int i = 0; i < int(svis.size()); i++) {
const Tangible *mt = mvis[i];
const Tangible *st = svis[i];
if (mt == nullptr) {
count += 1;
sb->write_int64(st->id());
}
// Get the actor in both models. s_actor may be nil.
const Tangible *s_actor = tangible_get(actor_id);
const Tangible *m_actor = master->tangible_get(actor_id);
assert(m_actor != nullptr);
// Calculate diffs.
tsb.write_int64(actor_id);
if (s_actor == nullptr) {
m_actor->id_player_pool_.serialize(&tsb);
m_actor->anim_queue_.serialize(&tsb);
} else {
s_actor->id_player_pool_.diff(m_actor->id_player_pool_, &tsb);
s_actor->anim_queue_.diff(m_actor->anim_queue_, &tsb);
}
sb->overwrite_int32(count_pos, count);
// For each tangible present in both models, compare
// the animation queues.
sb->write_int32(0);
count_pos = sb->total_writes();
count = 0;
for (int i = 0; i < int(svis.size()); i++) {
const Tangible *mt = mvis[i];
const Tangible *st = svis[i];
if ((mt != nullptr) && (st != nullptr)) {
if (st->anim_queue_.need_patch(mt->anim_queue_)) {
count++;
sb->write_int64(st->id());
st->anim_queue_.diff(mt->anim_queue_, sb);
}
}
}
sb->overwrite_int32(count_pos, count);
// Forward to client, and apply to server-synchronous.
tsb.copy_into(xsb);
patch_actor(&tsb);
assert(tsb.at_eof());
}
void World::patch_visible_animations(StreamBuffer *sb) {
void World::patch_visible(StreamBuffer *sb) {
// Receive create messages.
int count = sb->read_int32();
for (int i = 0; i < count; i++) {
@@ -881,187 +793,143 @@ void World::patch_visible_animations(StreamBuffer *sb) {
}
}
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(LS, tanobj);
const AnimStep &aqback = tan->anim_queue_.back();
LS.set(graphic, aqback.graphic());
LS.set(plane, aqback.plane());
LS.set(x, aqback.xyz().x);
LS.set(y, aqback.xyz().y);
LS.set(z, aqback.xyz().z);
LS.set(facing, aqback.facing());
return LS.result();
}
void World::diff_visible(const util::IdVector &visible, World *master, StreamBuffer *xsb) {
StreamBuffer tsb;
LuaDefine(tangible_animate, "c") {
LuaArg tanobj, config;
LuaStack LS(L, tanobj, config);
World *w = World::fetch_global_pointer(L);
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;
step.from_lua(L, config.index(), prev);
if (step.action() == "") {
luaL_error(L, "animation action must be specified");
}
tan->anim_queue_.add(id, step);
tan->update_plane_item();
return LS.result();
}
LuaDefine(tangible_setclass, "c") {
LuaArg tanobj, classname;
LuaVar classtab, mt;
LuaStack LS(L, tanobj, classname, classtab, mt);
World *w = World::fetch_global_pointer(L);
w->tangible_get(LS, tanobj);
LS.getclass(classtab, classname);
LS.getmetatable(mt, tanobj);
LS.rawset(mt, "__index", classtab);
return LS.result();
}
LuaDefine(tangible_delete, "c") {
LuaArg tanobj;
LuaStack LS(L, tanobj);
World *w = World::fetch_global_pointer(L);
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.");
return 0;
}
w->tangible_delete(tan->id());
return LS.result();
}
LuaDefine(tangible_build, "c") {
LuaArg config;
LuaVar classname, classtab, mt;
LuaRet database;
LuaStack LS(L, config, classname, classtab, database, mt);
// Get the specified tangibles in both models.
// Some tangibles may be missing in the master, some may be missing in the sync.
TanVector mvis = master->tangible_get_all(visible);
TanVector svis = tangible_get_all(visible);
assert(mvis.size() == svis.size());
LS.checktable(config);
// Get the class of the new tangible.
LS.rawget(classname, config, "class");
if (LS.isnil(classname)) {
luaL_error(L, "must specify a class name");
// For each tangible that exists in the master, but not
// in the synchronous model, send a create message.
tsb.write_int32(0);
int count_pos = tsb.total_writes();
int count = 0;
for (int i = 0; i < int(svis.size()); i++) {
const Tangible *mt = mvis[i];
const Tangible *st = svis[i];
if ((st == nullptr) && (mt != nullptr)) {
count += 1;
tsb.write_int64(mt->id());
mt->anim_queue_.serialize(&tsb);
}
}
LS.getclass(classtab, classname);
tsb.overwrite_int32(count_pos, count);
// Parse the initial animation step.
AnimStep initstep, blank;
initstep.from_lua(L, config.index(), blank);
if (!initstep.has_xyz()) {
luaL_error(L, "You must specify (X,Y,Z) for new tangible");
// For each tangible present in the synchronous model that doesn't
// exist in the master model, send command to delete it.
tsb.write_int32(0);
count_pos = tsb.total_writes();
count = 0;
for (int i = 0; i < int(svis.size()); i++) {
const Tangible *mt = mvis[i];
const Tangible *st = svis[i];
if ((mt == nullptr) && (st != nullptr)) {
count += 1;
tsb.write_int64(st->id());
}
}
if (!initstep.has_plane()) {
luaL_error(L, "You must specify plane for new tangible");
tsb.overwrite_int32(count_pos, count);
// For each tangible present in both models, compare
// the animation queues.
tsb.write_int32(0);
count_pos = tsb.total_writes();
count = 0;
for (int i = 0; i < int(svis.size()); i++) {
const Tangible *mt = mvis[i];
const Tangible *st = svis[i];
if ((mt != nullptr) && (st != nullptr)) {
if (st->anim_queue_.need_patch(mt->anim_queue_)) {
count++;
tsb.write_int64(st->id());
st->anim_queue_.diff(mt->anim_queue_, &tsb);
}
}
}
if (!initstep.has_graphic()) {
luaL_error(L, "You must specify graphic for new tangible");
tsb.overwrite_int32(count_pos, count);
// Forward to client, and apply to server-synchronous.
tsb.copy_into(xsb);
patch_visible(&tsb);
assert(tsb.at_eof());
// Copy the version number from master animqueue to synch animqueue.
for (int i = 0; i < int(mvis.size()); i++) {
const Tangible *m_tan = mvis[i];
if (m_tan != nullptr) {
Tangible *s_tan = const_cast<Tangible *>(svis[i]);
if (s_tan == nullptr) {
s_tan = tangible_get(m_tan->id());
assert(s_tan != nullptr);
}
s_tan->anim_queue_.update_version(m_tan->anim_queue_);
}
}
// TODO: generate error if there's extra crap in the config table.
World *w = World::fetch_global_pointer(L);
Tangible *tan = w->tangible_make(L, 0, true);
lua_replace(L, database.index());
// Update the class of the new tangible.
LS.getmetatable(mt, database);
LS.rawset(mt, "__index", classtab);
// Update the animation queue and planemap of the new tangible.
int64_t stepid = w->id_global_pool_.alloc_id_for_thread(L);
tan->anim_queue_.add(stepid, initstep);
tan->update_plane_item();
return LS.result();
}
LuaDefine(tangible_get, "c") {
LuaArg id;
LuaVar tangibles;
LuaRet database;
LuaStack LS(L, id, tangibles, database);
int64_t nid = LS.ckinteger(id);
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(database, tangibles, id);
if (!LS.istable(database)) {
luaL_error(L, "Not a tangible ID: %d", nid);
}
return LS.result();
void World::patch_luatabs(StreamBuffer *sb) {
int64_t actor_id = sb->read_int64();
util::HashValue closehash = sb->read_hashvalue();
int ncreate = sb->read_int32();
util::IdVector closetans = get_near(actor_id, RadiusClose, true);
assert(closehash == util::hash_id_vector(closetans));
number_lua_tables(closetans);
create_new_tables(ncreate);
patch_tangible_databases(sb);
patch_numbered_tables(sb);
unnumber_lua_tables();
}
LuaDefine(tangible_redirect, "c") {
LuaArg actor1, actor2, bldz;
LuaStack LS(L, actor1, actor2, bldz);
World *w = World::fetch_global_pointer(L);
bool bulldoze = LS.ckboolean(bldz);
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(LS, actor2);
tan2->configure_id_pool_for_actor();
w->redirects_[tan1->id()] = tan2->id();
}
if (bulldoze) {
w->tangible_delete(tan1->id());
}
return LS.result();
void World::diff_luatabs(int64_t actor_id, World *master, StreamBuffer *xsb) {
StreamBuffer tsb;
// Calculate the set of close tangibles.
util::IdVector closetans = master->get_near(actor_id, RadiusClose, true);
assert(get_near(actor_id, RadiusClose, true) == closetans);
util::HashValue closehash = util::hash_id_vector(closetans);
// Number and pair tables in the synchronous and master model.
number_lua_tables(closetans);
pair_lua_tables(closetans, master->state());
int ncreate = number_remaining_tables(closetans, master->state());
create_new_tables(ncreate);
// Difference transmit.
tsb.write_int64(actor_id);
tsb.write_hashvalue(closehash);
tsb.write_int32(ncreate);
diff_tangible_databases(closetans, master->state(), &tsb);
diff_numbered_tables(master->state(), &tsb);
// Patch
tsb.copy_into(xsb);
assert(tsb.read_int64() == actor_id);
assert(tsb.read_hashvalue() == closehash);
assert(tsb.read_int32() == ncreate);
patch_tangible_databases(&tsb);
patch_numbered_tables(&tsb);
unnumber_lua_tables();
assert(tsb.at_eof());
// Unnumber tables in both models.
unnumber_lua_tables();
master->unnumber_lua_tables();
}
LuaDefine(tangible_id, "c") {
LuaArg tanobj;
LuaRet id;
LuaStack LS(L, tanobj, id);
LS.set(id, World::tangible_id(LS, tanobj));
return LS.result();
void World::diff_everything(int64_t actor_id, World *master, StreamBuffer *sb) {
diff_actor(actor_id, master, sb);
util::IdVector visible = get_visible_union(actor_id, master);
diff_visible(visible, master, sb);
diff_luatabs(actor_id, master, sb);
}
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.");
}
return lua_yield(L, 1);
}
LuaDefine(world_getregistry, "f") {
lua_pushvalue(L, LUA_REGISTRYINDEX);
return 1;
}
LuaDefine(world_xtype, "f") {
LuaArg tab;
LuaRet rtype;
LuaStack LS(L, tab, rtype);
int xt = LS.xtype(tab);
LS.set(rtype, xt);
return LS.result();
}
LuaDefine(world_settabletype, "f") {
LuaArg tab, ttype;
LuaStack LS(L, tab, ttype);
if (!LS.istable(tab)) {
luaL_error(L, "Not a table");
}
int tt = LS.ckinteger(ttype);
if ((tt < LUA_TT_GENERAL) || (tt > LUA_TT_CLASS)) {
luaL_error(L, "table type out of range");
}
LS.settabletype(tab, tt);
return LS.result();
void World::patch_everything(StreamBuffer *sb) {
patch_actor(sb);
patch_visible(sb);
patch_luatabs(sb);
}
@@ -1072,16 +940,14 @@ static bool worlds_identical(const std::unique_ptr<World> &w1, const std::unique
return sbw1.contents_equal(&sbw2);
}
LuaDefine(unittests_worlddiffs, "c") {
std::unique_ptr<World> m, ss, cs;
LuaDefine(unittests_worldpairtab, "c") {
std::unique_ptr<World> m(new World(util::WORLD_TYPE_MASTER));
std::unique_ptr<World> ss(new World(util::WORLD_TYPE_S_SYNC));
StreamBuffer sb;
int ncreate;
// Test the numbering of lua tables. We create some general
// tables using tangible_set_string. Then we install some
// specialty tables (not numberable) using tangible_copy_global.
// Finally, we number and dump the list of numbered tables.
m.reset(new World(util::WORLD_TYPE_MASTER));
// Create a master model containing some general tables, and
// some specialty tables (not numberable).
m->tangible_make(0, 123, false);
m->tangible_set_string(123, "inventory.TID", "inventory");
m->tangible_set_string(123, "transactions.TID", "transactions");
@@ -1090,14 +956,9 @@ LuaDefine(unittests_worlddiffs, "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->number_lua_tables(util::id_vector_create(123));
LuaAssertStrEq(L, m->numbered_tables_debug_string(),
"inventory;inventory.cplx;skills;skills.leet;transactions;");
// Now we're going to create a synchronous model that's similar to, but not
// exactly the same as that master model, and we're going to pair the tables.
// Only some of these tables should pair: inventory, skills, and skills.leet
ss.reset(new World(util::WORLD_TYPE_S_SYNC));
// exactly the same as that master model.
ss->tangible_make(0, 123, false);
ss->tangible_set_string(123, "inventory.TID", "inventory");
ss->tangible_set_string(123, "skills.TID", "skills");
@@ -1105,6 +966,9 @@ LuaDefine(unittests_worlddiffs, "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");
// Now we're going to test the numbering and pairing of tables.
// Only these tables should pair: inventory, skills, and skills.leet
ss->number_lua_tables(util::id_vector_create(123));
LuaAssertStrEq(L, ss->numbered_tables_debug_string(),
"gltab;inventory;math;skills;skills.crap;skills.leet;");
@@ -1121,11 +985,15 @@ LuaDefine(unittests_worlddiffs, "c") {
ss->create_new_tables(ncreate);
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state()),
"inventory=inventory;inventory.cplx=unknown;skills=skills;skills.leet=skills.leet;transactions=unknown;");
return 0;
}
// Create new clean world models.
m.reset(new World(util::WORLD_TYPE_MASTER));
ss.reset(new World(util::WORLD_TYPE_S_SYNC));
cs.reset(new World(util::WORLD_TYPE_C_SYNC));
LuaDefine(unittests_worldanimdiff, "c") {
std::unique_ptr<World> m(new World(util::WORLD_TYPE_MASTER));
std::unique_ptr<World> ss(new World(util::WORLD_TYPE_S_SYNC));
std::unique_ptr<World> cs(new World(util::WORLD_TYPE_C_SYNC));
StreamBuffer sb;
util::IdVector ids = util::id_vector_create(123, 345);
// Create some tangibles, and add some animations.
m->tangible_make(0, 123, false);
@@ -1141,8 +1009,8 @@ LuaDefine(unittests_worlddiffs, "c") {
"id=771 action=walkto x=6 y=2; ");
// Now difference transmit all that to the client.
ss->difference_transmit(123, m.get(), &sb);
cs->apply_differences(&sb);
ss->diff_visible(ids, m.get(), &sb);
cs->patch_visible(&sb);
LuaAssertStrEq(L, ss->tangible_ids_debug_string(), "123,345");
LuaAssertStrEq(L, ss->tangible_anim_debug_string(123),
"id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=; "
@@ -1165,8 +1033,8 @@ LuaDefine(unittests_worlddiffs, "c") {
"id=773 action=walkto x=2 y=5; ");
// Now difference transmit all that to the client again.
ss->difference_transmit(123, m.get(), &sb);
cs->apply_differences(&sb);
ss->diff_visible(ids, m.get(), &sb);
cs->patch_visible(&sb);
LuaAssertStrEq(L, ss->tangible_anim_debug_string(123),
"id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=; "
"id=770 action=walkto x=3 y=4; "
@@ -1182,8 +1050,8 @@ LuaDefine(unittests_worlddiffs, "c") {
LuaAssertStrEq(L, m->tangible_ids_debug_string(), "123");
// And difference transmit
ss->difference_transmit(123, m.get(), &sb);
cs->apply_differences(&sb);
ss->diff_visible(ids, m.get(), &sb);
cs->patch_visible(&sb);
LuaAssertStrEq(L, ss->tangible_ids_debug_string(), "123");
LuaAssert(L, worlds_identical(ss, cs));

View File

@@ -197,20 +197,7 @@ public:
void snapshot();
void rollback();
// Difference transmission.
//
// This generates diffs and stores them in the specified buffer,
// so that they can be sent to the client. It also applies the diffs
// to this model.
//
void difference_transmit(int64_t actor, World *master, StreamBuffer *sb);
// Apply differences.
//
// Note that difference_transmit applies the differences to the server
// synchronous model, so this is only used by the client synchronous model.
//
void apply_differences(StreamBuffer *sb);
util::IdVector get_visible_union(int64_t actor_id, World *master);
public:
////////////////////////////////////////////////////////////////////////////
@@ -252,6 +239,10 @@ public:
//
void tangible_copy_global(int64_t id, const std::string &path, const std::string &global);
// Pretty-print the entire tangible database and return it as a string.
//
std::string tangible_pprint(int64_t id) const;
private:
// Run any threads which according to the scheduler queue are ready.
//
@@ -269,21 +260,26 @@ private:
//
void invoke_plan(int64_t actor_id, int64_t place_id, const std::string &action, const InvocationData &idata);
// pass 1 of difference transmission: actor essentials.
public:
///////////////////////////////////////////////////////////
//
// Before we do anything else, we need to get the actor in the right place.
// We also update the actor's ID allocation pipeline.
// Difference transmission
//
static void diff_actor_essentials(const Tangible *mactor, const Tangible *sactor, StreamBuffer *sb);
int64_t patch_actor_essentials(StreamBuffer *sb);
///////////////////////////////////////////////////////////
// Pass 2 of difference transmission: visible animations.
int64_t patch_actor(StreamBuffer *sb);
void diff_actor(int64_t actor_id, World *master, StreamBuffer *sb);
void patch_visible(StreamBuffer *sb);
void diff_visible(const util::IdVector &ids, World *master, StreamBuffer *sb);
void patch_luatabs(StreamBuffer *sb);
void diff_luatabs(int64_t actor_id, World *master, StreamBuffer *sb);
// This is the main entry point for difference transmission.
//
// Synchronizes the animation status of every tangible inside the visibility
// radius of either model. Creates missing tangibles and deletes excess tangibles.
//
static void diff_visible_animations(const TanVector &mvis, const TanVector &svis, StreamBuffer *sb);
void patch_visible_animations(StreamBuffer *sb);
void patch_everything(StreamBuffer *sb);
void diff_everything(int64_t actor, World *master, StreamBuffer *sb);
public:
///////////////////////////////////////////////////////////
@@ -350,17 +346,11 @@ public:
//
///////////////////////////////////////////////////////////
// Compare the numbered general tables.
//
void patch_numbered_tables(StreamBuffer *sb);
void diff_numbered_tables(lua_State *master, StreamBuffer *sb);
// Compare the tangible databases.
//
void diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb);
void patch_numbered_tables(StreamBuffer *sb);
void patch_tangible_databases(StreamBuffer *sb);
void diff_tangible_databases(const IdVector &basis, lua_State *master, StreamBuffer *sb);
private:
// Type of model