diff --git a/luprex/cpp/core/luastack.cpp b/luprex/cpp/core/luastack.cpp index 1f30cf7b..ccd09706 100644 --- a/luprex/cpp/core/luastack.cpp +++ b/luprex/cpp/core/luastack.cpp @@ -376,6 +376,54 @@ void LuaStack::makeclass(LuaSlot tab, std::string_view name) const { lua_pop(L_, 1); } +void LuaStack::maketan(LuaSlot tab, int64_t id) const { + LuaVar tangibles, metatab; + LuaStack LS(L_, tangibles, metatab); + + // Try to get the existing tangible. + LS.rawget(tangibles, LuaRegistry, "tangibles"); + LS.rawget(tab, tangibles, id); + + // If we succeeded, return it. + if (LS.istable(tab)) { + LS.result(); + return; + } + + // Create the tangible's database and metatable. + LS.set(tab, LuaNewTable); + LS.set(metatab, LuaNewTable); + LS.setmetatable(tab, metatab); + + // Mark the tangible using the tabletype field. + LS.settabletype(tab, LUA_TT_TANGIBLE); + LS.settabletype(metatab, LUA_TT_TANGIBLEMETA); + + // Store the tangible ID and lock the metatable. + LS.rawset(metatab, "id", id); + LS.rawset(metatab, "__metatable", false); + + // Store the database into the tangibles table. + LS.rawset(tangibles, id, tab); + + LS.result(); +} + + +bool LuaStack::tanblank(LuaSlot tab) const { + bool result = true; + if (istable(tab) && gettabletype(tab) == LUA_TT_TANGIBLE) { + if (lua_getmetatable(L_, tab.index())) { + lua_pushstring(L_, "threads"); + lua_rawget(L_, -2); + if (lua_type(L_, -1) == LUA_TTABLE) { + result = false; + } + lua_pop(L_, 2); + } + } + return result; +} int64_t LuaStack::tanid(LuaSlot tab) const { int64_t result = 0; diff --git a/luprex/cpp/core/luastack.hpp b/luprex/cpp/core/luastack.hpp index 01fae478..b68a66e5 100644 --- a/luprex/cpp/core/luastack.hpp +++ b/luprex/cpp/core/luastack.hpp @@ -222,9 +222,8 @@ int LuaTypeTagValue(lua_State *L) { return 0; } #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 +#define LUA_TT_GLOBALDB 21 +#define LUA_TT_CLASS 22 // World types enum. @@ -458,8 +457,16 @@ public: void makeclass(LuaSlot tab, LuaSlot name) const; void makeclass(LuaSlot tab, std::string_view name) const; - // Get the ID of a tangible. It's a little weird to put this in - // this module. + // Create a tangible, or look up an existing tangible. + // This doesn't do the entire process of tangible creation. It + // just creates an empty table, marks it as a tangible, creates + // the metatable, stores the tangible ID, and stores it in the tangible table. + void maketan(LuaSlot tab, int64_t id) const; + + // Return true if a tangible is empty (deleted or not yet created). + bool tanblank(LuaSlot tab) const; + + // Get the ID of a tangible. int64_t tanid(LuaSlot tab) const; // Return true if the value is a sortable key (string, number, or boolean). diff --git a/luprex/cpp/core/pprint.cpp b/luprex/cpp/core/pprint.cpp index 80f74be3..ab778790 100644 --- a/luprex/cpp/core/pprint.cpp +++ b/luprex/cpp/core/pprint.cpp @@ -78,6 +78,10 @@ public: (*output_) << ""; return; } + case LUA_TT_TANGIBLEMETA: { + (*output_) << ""; + return; + } default: { (*output_) << ""; return; @@ -103,17 +107,15 @@ public: // Determine the extended type of the object. If the // expand flag is true, try to coerce it to a general table. - int type = LS.xtype(value); - if (expand && (LS.istable(value))) { - type = LUA_TT_GENERAL; - } + int xtype = LS.xtype(value); - // If it's anything but a general table, use 'atomic_print' - // and return. - if (type != LUA_TT_GENERAL) { - atomic_print(type, value, true); - LS.result(); - return; + // Print the atomic portion. + if (xtype != LUA_TT_GENERAL) { + atomic_print(xtype, value, true); + if ((xtype < LUA_TT_GENERAL) || (!expand)) { + LS.result(); + return; + } } // Find out whether it has a table number. If necessary, @@ -189,7 +191,7 @@ public: // Output the metatable. LS.getmetatable(val, value); - if (LS.istable(val)) { + if (LS.istable(val) && (LS.gettabletype(val) != LUA_TT_TANGIBLEMETA)) { multiline = true; if (needcomma) (*output_) << ","; needcomma = true; diff --git a/luprex/cpp/core/world-accessor.cpp b/luprex/cpp/core/world-accessor.cpp index f41f260e..b98107ac 100644 --- a/luprex/cpp/core/world-accessor.cpp +++ b/luprex/cpp/core/world-accessor.cpp @@ -26,7 +26,7 @@ LuaDefine(tangible_animstate, "tan", 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); + Tangible *tan = w->tangible_get(LS, tanobj, false); const AnimStep &aqback = tan->anim_queue_.back(); LS.set(graphic, aqback.graphic()); LS.set(plane, aqback.plane()); @@ -44,7 +44,7 @@ LuaDefine(tangible_xyz, "tan", LuaRet x, y, z; LuaStack LS(L, tanobj, x, y, z); World *w = World::fetch_global_pointer(L); - Tangible *tan = w->tangible_get(LS, tanobj); + Tangible *tan = w->tangible_get(LS, tanobj, false); const AnimStep &aqback = tan->anim_queue_.back(); LS.set(x, aqback.xyz().x); LS.set(y, aqback.xyz().y); @@ -60,7 +60,7 @@ LuaDefine(tangible_animate, "tan,configtable", LuaStack LS(L, tanobj, config); LuaKeywordParser kp(LS, config); World *w = World::fetch_global_pointer(L); - Tangible *tan = w->tangible_get(LS, tanobj); + Tangible *tan = w->tangible_get(LS, tanobj, false); int64_t id = w->alloc_id_predictable(); AnimStep step; step.configure(kp, tan->anim_queue_.back()); @@ -82,7 +82,7 @@ LuaDefine(tangible_setclass, "tan,class", LuaVar classtab, mt; LuaStack LS(L, tanobj, classname, classtab, mt); World *w = World::fetch_global_pointer(L); - w->tangible_get(LS, tanobj); + w->tangible_get(LS, tanobj, false); eng::string err = LS.getclass(classtab, classname); if (err != "") { luaL_error(L, "%s", err.c_str()); @@ -101,7 +101,7 @@ LuaDefine(tangible_getclass, "tan", LuaRet classname; LuaStack LS(L, tanobj, mt, classtab, classname); World *w = World::fetch_global_pointer(L); - w->tangible_get(LS, tanobj); + w->tangible_get(LS, tanobj, false); LS.getmetatable(mt, tanobj); LS.rawget(classtab, mt, "__index"); eng::string name = LS.classname(classtab); @@ -120,8 +120,10 @@ LuaDefine(tangible_delete, "tan", 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. + Tangible *tan = w->tangible_get(LS, tanobj, true); + if (tan == nullptr) { + return LS.result(); + } if (tan->is_an_actor()) { luaL_error(L, "Cannot delete a player using tangible.delete, use tangible.redirect instead."); return 0; @@ -205,14 +207,14 @@ LuaDefine(tangible_redirect, "tan1,tan2,bulldozetan1", 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); + Tangible *tan1 = w->tangible_get(LS, actor1, false); 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); + Tangible *tan2 = w->tangible_get(LS, actor2, false); tan2->configure_id_pool_for_actor(); w->redirects_[tan1->id()] = tan2->id(); } @@ -268,7 +270,7 @@ LuaDefine(tangible_near, "tan,radius,omit_nowhere,omit_self", LuaRet list; LuaStack LS(L, ltan, lradius, lomit_nowhere, lomit_self, list); World *w = World::fetch_global_pointer(L); - Tangible *tan = w->tangible_get(LS, ltan); + Tangible *tan = w->tangible_get(LS, ltan, false); const AnimStep &aqback = tan->anim_queue_.back(); PlaneScan scan; @@ -460,13 +462,20 @@ LuaDefine(tangible_start, "tangible,function,arg1,arg2...", } } + // Check the entire tangible list for validity. + for (int i = 1; ; i++) { + LS.rawget(place, tanlist, i); + if (LS.isnil(place)) break; + w->tangible_get(LS, place, false); + } + + // Start threads on all the tangibles. for (int i = 1; ; i++) { LS.rawget(place, tanlist, i); if (LS.isnil(place)) break; // Confirm that the place is a valid tangible, // and get the tangible ID. - w->tangible_get(LS, place); place_id = LS.tanid(place); // Get place's metatable and threads table. diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index 4b7b27f2..62c5dd1b 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -134,14 +134,16 @@ World::TanVector World::tangible_get_all(const IdVector &ids) const { return result; } -Tangible *World::tangible_get(const LuaStack &LS, LuaSlot tab) { +Tangible *World::tangible_get(const LuaStack &LS, LuaSlot tab, bool allowdel) { int64_t id = LS.tanid(tab); if (id == 0) { luaL_error(LS.state(), "parameter is not a tangible"); } Tangible *result = tangible_get(id); - if (result == nullptr) { - luaL_error(LS.state(), "parameter is not a tangible"); + if (!allowdel) { + if (result == nullptr) { + luaL_error(LS.state(), "argument is a deleted tangible, which is not allowed here"); + } } return result; } @@ -154,9 +156,9 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, const eng::string &plan } assert(id != 0); - LuaVar tangibles, metatab; + LuaVar metatab; LuaRet database; - LuaStack LS(L, tangibles, database, metatab); + LuaStack LS(L, database, metatab); // Create the C++ part of the structure. UniqueTangible &t = tangibles_[id]; @@ -167,25 +169,14 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, const eng::string &plan t->anim_queue_.clear(plane); t->update_plane_item(); - // Create the tangible's database and metatable. - LS.set(database, LuaNewTable); - LS.set(metatab, LuaNewTable); - LS.setmetatable(database, metatab); + // Fetch the tangible's Lua database and metatable. + LS.maketan(database, id); + LS.getmetatable(metatab, database); - // 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. + // Set up the inventory and thread table. 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(); @@ -193,8 +184,8 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, const eng::string &plan void World::tangible_delete(int64_t id) { lua_State *L = state(); - LuaVar tangibles, database; - LuaStack LS(L, tangibles, database); + LuaVar tangibles, database, metatab; + LuaStack LS(L, tangibles, database, metatab); // Fetch the C++ side of the tangible. auto iter = tangibles_.find(id); @@ -204,16 +195,17 @@ void World::tangible_delete(int64_t id) { } // Fetch the lua side of the tangible. - LS.rawget(tangibles, LuaRegistry, "tangibles"); - LS.rawget(database, tangibles, id); + LS.maketan(database, id); assert(LS.istable(database)); - - // Clear out the database. - LS.cleartable(database, true); - LS.settabletype(database, LUA_TT_DEADTANGIBLE); + LS.getmetatable(metatab, database); - // Remove the lua portion from the tangibles table. - LS.rawset(tangibles, id, LuaNil); + // Clear out the database and the metatable. + LS.cleartable(database, false); + LS.cleartable(metatab, true); + + // Now put the bare minimum info back into the metatable. + LS.rawset(metatab, "id", id); + LS.rawset(metatab, "__metatable", false); // Remove the C++ portion from the tangibles table. tangibles_.erase(iter); diff --git a/luprex/cpp/core/world-difftab.cpp b/luprex/cpp/core/world-difftab.cpp index d7410895..c286191f 100644 --- a/luprex/cpp/core/world-difftab.cpp +++ b/luprex/cpp/core/world-difftab.cpp @@ -304,12 +304,7 @@ static void set_transmitted_value(LuaStack &LS, LuaSlot tangibles, LuaSlot ntmap case LUA_TT_TANGIBLE: { int64_t id = sb->read_int64(); DebugLine(dbc) << dbinfo << "tan " << id; - LS.rawget(target, tangibles, id); - if (LS.isnil(target)) { - World *w = World::fetch_global_pointer(LS.state()); - w->tangible_make(LS.state(), id, "nowhere", true); - lua_replace(LS.state(), target.index()); - } + LS.maketan(target, id); return; } case LUA_TT_GLOBALENV: { diff --git a/luprex/cpp/core/world-diffxmit.cpp b/luprex/cpp/core/world-diffxmit.cpp index abb60d52..edc76abf 100644 --- a/luprex/cpp/core/world-diffxmit.cpp +++ b/luprex/cpp/core/world-diffxmit.cpp @@ -1,10 +1,11 @@ -#include "world.hpp" #include +#include "world.hpp" + util::IdVector World::get_visible_union(int64_t actor_id, World *master) { return util::sort_union_id_vectors( - master->get_near(actor_id, RadiusVisibility, true, false, false), - get_near(actor_id, RadiusVisibility, true, false, false)); + master->get_near(actor_id, RadiusVisibility, true, false, false), + get_near(actor_id, RadiusVisibility, true, false, false)); } int64_t World::patch_actor(StreamBuffer *sb, DebugCollector *dbc) { @@ -27,14 +28,14 @@ int64_t World::patch_actor(StreamBuffer *sb, DebugCollector *dbc) { return actor_id; } -void World::diff_actor(int64_t actor_id, World *master, StreamBuffer *xsb) { +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) { @@ -85,15 +86,17 @@ void World::patch_visible(StreamBuffer *sb, DebugCollector *dbc) { } } -void World::diff_visible(const util::IdVector &visible, World *master, StreamBuffer *xsb) { +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. + // 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); @@ -169,11 +172,13 @@ void World::patch_luatabs(StreamBuffer *sb, DebugCollector *dbc) { 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); + util::IdVector closetans = + get_near(actor_id, RadiusClose, true, false, true); assert(closehash == util::hash_id_vector(closetans)); number_lua_tables(closetans); create_new_tables(ncreate); - // DebugLine(dbc) << "lua tables: " << nt << " existing " << ncreate << " new"; + // DebugLine(dbc) << "lua tables: " << nt << " existing " << ncreate << " + // new"; patch_tangible_databases(sb, dbc); patch_numbered_tables(sb, dbc); unnumber_lua_tables(); @@ -183,7 +188,8 @@ 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, true); + util::IdVector closetans = + master->get_near(actor_id, RadiusClose, true, false, true); assert(get_near(actor_id, RadiusClose, true, false, true) == closetans); util::HashValue closehash = util::hash_id_vector(closetans); @@ -199,7 +205,7 @@ void World::diff_luatabs(int64_t actor_id, World *master, StreamBuffer *xsb) { 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); @@ -207,7 +213,7 @@ void World::diff_luatabs(int64_t actor_id, World *master, StreamBuffer *xsb) { assert(tsb.read_int32() == ncreate); patch_tangible_databases(&tsb, nullptr); patch_numbered_tables(&tsb, nullptr); - assert(tsb.empty()); + assert(tsb.empty()); // Unnumber tables in both models. unnumber_lua_tables(); @@ -249,8 +255,10 @@ void World::diff_tanclass(int64_t actor_id, World *master, StreamBuffer *xsb) { 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); + // 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); tsb.write_int32(0); int write_count_after = tsb.total_writes(); @@ -310,7 +318,7 @@ int64_t World::patch_everything(StreamBuffer *sb, DebugCollector *dbc) { return actor_id; } -void World::diff_everything(int64_t actor_id, World *master, StreamBuffer *sb) { +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 --git a/luprex/cpp/core/world-testing.cpp b/luprex/cpp/core/world-testing.cpp index 4dff9b01..8fe81c0f 100644 --- a/luprex/cpp/core/world-testing.cpp +++ b/luprex/cpp/core/world-testing.cpp @@ -55,14 +55,11 @@ eng::string World::tangible_pprint(int64_t id) const { LS.rawget(tan, tangibles, id); eng::ostringstream oss; if (LS.istable(tan)) { - LS.getmetatable(meta, tan); - LS.clearmetatable(tan); PrettyPrintOptions opts; opts.indent = false; pprint(LS, tan, opts, &oss); - LS.setmetatable(tan, meta); } else { - oss << ""; + oss << "{}"; } LS.result(); return oss.str(); @@ -384,7 +381,7 @@ LuaDefine(unittests_world3diffluatab, "", "some unit tests") { // The data in the master model should now look like this: const char *expect_123 = - "{ " + "{ " "bacon='crispy', " "inventory={ gold='wealthy' }, " "skills={ " @@ -393,7 +390,7 @@ LuaDefine(unittests_world3diffluatab, "", "some unit tests") { "} " "}"; const char *expect_345 = - "{ " + "{ " "inventory={ gold='poor' }, " "phone='867-5309' " "}"; diff --git a/luprex/cpp/core/world.hpp b/luprex/cpp/core/world.hpp index 6c416105..bf584fc8 100644 --- a/luprex/cpp/core/world.hpp +++ b/luprex/cpp/core/world.hpp @@ -137,17 +137,21 @@ public: // Get a pointer to the specified tangible. // - // If there's no such tangible, returns nullptr. + // If there's no such tangible, or if the tangible is deleted, + // returns nullptr. // Tangible *tangible_get(int64_t id); const Tangible *tangible_get(int64_t id) const; // Get a pointer to the specified tangible. // - // The value on the lua stack should be a valid lua tangible. If not, - // a lua error is generated. + // The value on the lua stack should be a lua tangible. // - Tangible *tangible_get(const LuaStack &LS, LuaSlot slot); + // If the 'allowdel' flag is true, then it is valid to pass in + // a deleted tangible. In that case, this function returns nullptr, + // but this is not a Lua error. + // + Tangible *tangible_get(const LuaStack &LS, LuaSlot slot, bool allowdel); // Get pointers to many tangibles. //