#include "world.hpp" #include 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, false), get_near_unsorted(actor_id, RadiusVisibility, true, false)); } 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(nullptr, actor_id, "", false); 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(state(), id, "", false); 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); } } 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); 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); } } 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(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_); } } } 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); std::cerr << "Sync closetans=" << util::id_vector_debug_string(closetans) << std::endl; 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 closetans = master->get_near(actor_id, RadiusClose, true, false); std::cerr << "Master closetans=" << util::id_vector_debug_string(closetans) << std::endl; assert(get_near(actor_id, RadiusClose, true, false) == 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); // 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; LuaStack 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); std::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); } } LS.result(); } void World::diff_tanclass(int64_t actor_id, World *master, StreamBuffer *xsb) { StreamBuffer tsb; LuaVar stangibles, mtangibles, stab, mtab, smeta, mmeta, sclass, mclass; LuaStack SLS(state(), stangibles, stab, smeta, sclass); LuaStack 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); 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"); std::string mname = MLS.classname(mclass); std::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); SLS.result(); MLS.result(); // 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"); std::cerr << "Before PS ids=" << tangible_ids_debug_string() << std::endl; bool modified = source_db_.patch(sb, dbc); if (modified) { std::string errs = source_db_.rebuild(); DebugLine(dbc) << "Source DB rebuilt"; // TODO: I don't currently have any good place to send the // error messages. This is a stopgap. std::cerr << errs; std::cerr << "After PS ids=" << tangible_ids_debug_string() << std::endl; } } 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()); } int64_t World::patch_everything(StreamBuffer *sb, DebugCollector *dbc) { DebugBlock dbb(dbc, "patch_everything"); int64_t actor_id = patch_actor(sb, dbc); patch_visible(sb, dbc); patch_luatabs(sb, dbc); patch_tanclass(sb, dbc); patch_source(sb, dbc); return actor_id; } 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); diff_tanclass(actor_id, master, sb); diff_source(master, sb); }