Files
integration/luprex/cpp/core/world-diffxmit.cpp

386 lines
13 KiB
C++

#include "world.hpp"
#include "serializelua.hpp"
util::IdVector World::get_visible_union(int64_t actor_id, World *master) {
util::IdVector v1, v2;
master->get_near(actor_id, RadiusVisibility, true, false, false, &v1);
get_near(actor_id, RadiusVisibility, true, false, false, &v2);
return util::sort_union_id_vectors(v1, v2);
}
int64_t World::patch_actor(StreamBuffer *sb, DebugCollector *dbc) {
DebugBlock dbb(dbc, "patch_actor");
int64_t actor_id = sb->read_int64();
Tangible *s_actor = tangible_get(actor_id);
if (s_actor == nullptr) {
DebugLine(dbc) << "create new actor " << actor_id;
s_actor = tangible_make(actor_id);
s_actor->id_player_pool_.deserialize(sb);
s_actor->anim_queue_.deserialize(sb);
s_actor->print_buffer_.deserialize(sb);
} else {
DebugHeader(dbc) << "patching actor " << actor_id << ":";
s_actor->id_player_pool_.patch(sb, dbc);
s_actor->anim_queue_.patch(sb, dbc);
s_actor->print_buffer_.patch(sb, dbc);
}
s_actor->update_plane_item();
return actor_id;
}
void World::diff_actor(int64_t actor_id, World *master, StreamBuffer *xsb) {
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);
// Calculate diffs.
tsb.write_int64(actor_id);
if (s_actor == nullptr) {
m_actor->id_player_pool_.serialize(&tsb);
m_actor->anim_queue_.serialize(&tsb);
m_actor->print_buffer_.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);
s_actor->print_buffer_.diff(m_actor->print_buffer_, &tsb);
}
// Forward to client, and apply to server-synchronous.
tsb.copy_into(xsb);
patch_actor(&tsb, nullptr);
assert(tsb.empty());
}
void World::patch_visible(StreamBuffer *sb, DebugCollector *dbc) {
DebugBlock dbb(dbc, "patch_visible");
// Receive create messages.
int count = sb->read_int32();
for (int i = 0; i < count; i++) {
int64_t id = sb->read_int64();
DebugLine(dbc) << "patch_visible create tan " << id;
Tangible *t = tangible_make(id);
t->anim_queue_.deserialize(sb);
t->update_plane_item();
}
// Receive delete messages
count = sb->read_int32();
for (int i = 0; i < count; i++) {
int64_t id = sb->read_int64();
DebugLine(dbc) << "patch_visible delete tan " << id;
tangible_delete(id);
}
// Receive update messages
count = sb->read_int32();
for (int i = 0; i < count; i++) {
int64_t id = sb->read_int64();
DebugLine(dbc) << "patch_visible animqueue tan " << id;
Tangible *t = tangible_get(id);
assert(t != nullptr);
t->anim_queue_.patch(sb, dbc);
t->update_plane_item();
}
}
void World::diff_visible(const util::IdVector &visible, World *master,
StreamBuffer *xsb) {
StreamBuffer tsb;
// 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());
// For each tangible that exists in the master, but not
// in the synchronous model, send a create message.
tsb.write_int32(0);
int64_t 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);
}
}
tsb.overwrite_int32(count_pos, count);
// 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());
}
}
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)) {
int64_t unwind = tsb.total_writes();
tsb.write_int64(st->id());
if (st->anim_queue_.diff(mt->anim_queue_, &tsb)) {
count++;
} else {
tsb.unwrite_to(unwind);
}
}
}
tsb.overwrite_int32(count_pos, count);
// Forward to client, and apply to server-synchronous.
tsb.copy_into(xsb);
patch_visible(&tsb, nullptr);
assert(tsb.empty());
// 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);
}
}
}
}
void World::patch_luatabs(StreamBuffer *sb, DebugCollector *dbc) {
DebugBlock dbb(dbc, "patch_luatabs");
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, false, true, &closetans);
assert(closehash == util::hash_id_vector(closetans));
number_lua_tables(closetans);
create_new_tables(ncreate);
// DebugLine(dbc) << "lua tables: " << nt << " existing " << ncreate << "
// new";
patch_tangible_databases(sb, dbc);
patch_numbered_tables(sb, dbc);
unnumber_lua_tables();
}
void World::diff_luatabs(int64_t actor_id, World *master, StreamBuffer *xsb) {
StreamBuffer tsb;
// Calculate the set of close tangibles.
util::IdVector mclosetans, sclosetans;
master->get_near(actor_id, RadiusClose, true, false, true, &mclosetans);
get_near(actor_id, RadiusClose, true, false, true, &sclosetans);
assert(mclosetans == sclosetans);
util::HashValue closehash = util::hash_id_vector(mclosetans);
// Number and pair tables in the synchronous and master model.
number_lua_tables(mclosetans);
pair_lua_tables(mclosetans, master->state());
int ncreate = number_remaining_tables(mclosetans, master->state());
create_new_tables(ncreate);
// Difference transmit.
tsb.write_int64(actor_id);
tsb.write_hashvalue(closehash);
tsb.write_int32(ncreate);
diff_tangible_databases(mclosetans, master->state(), &tsb);
diff_numbered_tables(master->state(), &tsb);
// Forward to client, and apply to server-synchronous.
tsb.copy_into(xsb);
assert(tsb.read_int64() == actor_id);
assert(tsb.read_hashvalue() == closehash);
assert(tsb.read_int32() == ncreate);
patch_tangible_databases(&tsb, nullptr);
patch_numbered_tables(&tsb, nullptr);
assert(tsb.empty());
// Unnumber tables in both models.
unnumber_lua_tables();
master->unnumber_lua_tables();
}
void World::patch_tanclass(StreamBuffer *sb, DebugCollector *dbc) {
DebugBlock dbb(dbc, "patch_tanclass");
lua_State *L = state();
LuaVar tangibles, tab, meta, sclass;
LuaExtStack LS(L, tangibles, tab, meta, sclass);
LS.rawget(tangibles, LuaRegistry, "tangibles");
int nmodified = sb->read_int32();
for (int i = 0; i < nmodified; i++) {
int64_t id = sb->read_int64();
LS.rawget(tab, tangibles, id);
assert(LS.istable(tab));
LS.getmetatable(meta, tab);
eng::string name = sb->read_string();
DebugLine(dbc) << "tanclass " << id << "=" << name;
if (name == "") {
LS.rawset(meta, "__index", LuaNil);
} else {
LS.makeclass(sclass, name);
LS.rawset(meta, "__index", sclass);
}
}
}
void World::diff_tanclass(int64_t actor_id, World *master, StreamBuffer *xsb) {
StreamBuffer tsb;
{
LuaVar stangibles, mtangibles, stab, mtab, smeta, mmeta, sclass, mclass;
LuaExtStack SLS(state(), stangibles, stab, smeta, sclass);
LuaExtStack MLS(master->state(), mtangibles, mtab, mmeta, mclass);
SLS.rawget(stangibles, LuaRegistry, "tangibles");
MLS.rawget(mtangibles, LuaRegistry, "tangibles");
// Calculate the set of close tangibles.
// TODO: we've already calculated this in an earlier function. This is
// wasteful.
util::IdVector closetans;
master->get_near(actor_id, RadiusClose, true, false, true, &closetans);
tsb.write_int32(0);
int write_count_after = tsb.total_writes();
int nmodified = 0;
for (int64_t id : closetans) {
MLS.rawget(mtab, mtangibles, id);
SLS.rawget(stab, stangibles, id);
MLS.getmetatable(mmeta, mtab);
SLS.getmetatable(smeta, stab);
MLS.rawget(mclass, mmeta, "__index");
SLS.rawget(sclass, smeta, "__index");
eng::string mname = MLS.classname(mclass);
eng::string sname = SLS.classname(sclass);
if (mname != sname) {
tsb.write_int64(id);
tsb.write_string(mname);
nmodified += 1;
}
}
tsb.overwrite_int32(write_count_after, nmodified);
}
// Forward to client, and apply to server-synchronous.
tsb.copy_into(xsb);
patch_tanclass(&tsb, nullptr);
assert(tsb.empty());
}
void World::patch_source(StreamBuffer *sb, DebugCollector *dbc) {
DebugBlock dbb(dbc, "patch_source");
bool modified = source_db_.patch(sb, dbc);
if (modified) {
rebuild_sourcedb();
DebugLine(dbc) << "Source DB rebuilt";
}
}
void World::diff_source(World *master, StreamBuffer *sb) {
StreamBuffer tsb;
source_db_.diff(master->source_db_, &tsb);
tsb.copy_into(sb);
patch_source(&tsb, nullptr);
assert(tsb.empty());
}
void World::patch_globals(StreamBuffer *sb, DebugCollector *dbc) {
DebugBlock dbb(dbc, "patch_globals");
int64_t seqno = sb->read_int64();
int32_t total = sb->read_int32();
if (total > 0) {
for (int i = 0; i < total; i++) {
eng::string gvar = sb->read_string();
eng::string serial = sb->read_string();
gvname_to_serial_[gvar] = serial;
}
}
assign_seqno_ = seqno;
gvname_to_seqno_.clear();
seqno_to_gvname_.clear();
gvname_modified_.clear();
}
void World::diff_globals(World *master, StreamBuffer *sb) {
StreamBuffer tsb;
tsb.write_int64(master->assign_seqno_);
tsb.write_int32(0);
int64_t count_pos = tsb.total_writes();
int32_t total_mods = 0;
for (const eng::string &gvar : gvname_modified_) {
const eng::string &mval = master->get_global(gvar);
const eng::string &sval = get_global(gvar);
if (mval != sval) {
total_mods += 1;
tsb.write_string(gvar);
tsb.write_string(mval);
}
}
auto iter = master->seqno_to_gvname_.lower_bound(assign_seqno_);
while (iter != master->seqno_to_gvname_.end()) {
const auto &gvar = iter->second;
if (gvname_modified_.find(gvar) == gvname_modified_.end()) {
const eng::string &mval = master->get_global(gvar);
const eng::string &sval = get_global(gvar);
if (mval != sval) {
total_mods += 1;
tsb.write_string(gvar);
tsb.write_string(mval);
}
}
iter++;
}
tsb.overwrite_int32(count_pos, total_mods);
tsb.copy_into(sb);
patch_globals(&tsb, nullptr);
assert(tsb.empty());
}
int64_t World::patch(StreamBuffer *sb, DebugCollector *dbc) {
DebugBlock dbb(dbc, "patch");
int64_t actor_id = patch_actor(sb, dbc);
patch_visible(sb, dbc);
bool full = sb->read_bool();
if (full) {
patch_luatabs(sb, dbc);
patch_tanclass(sb, dbc);
patch_source(sb, dbc);
patch_globals(sb, dbc);
}
return actor_id;
}
void World::diff(int64_t actor_id, bool full, World *master, StreamBuffer *sb) {
diff_actor(actor_id, master, sb);
util::IdVector visible = get_visible_union(actor_id, master);
diff_visible(visible, master, sb);
sb->write_bool(full);
if (full) {
diff_luatabs(actor_id, master, sb);
diff_tanclass(actor_id, master, sb);
diff_source(master, sb);
diff_globals(master, sb);
}
}