Change the creation/deletion of tangibles so that the tangible database is never really deleted.

This commit is contained in:
2023-03-16 23:31:29 -04:00
parent 86a27ef2d4
commit a8c780563f
9 changed files with 152 additions and 90 deletions

View File

@@ -376,6 +376,54 @@ void LuaStack::makeclass(LuaSlot tab, std::string_view name) const {
lua_pop(L_, 1); 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 LuaStack::tanid(LuaSlot tab) const {
int64_t result = 0; int64_t result = 0;

View File

@@ -222,9 +222,8 @@ int LuaTypeTagValue(lua_State *L) { return 0; }
#define LUA_TT_GLOBALENV 18 #define LUA_TT_GLOBALENV 18
#define LUA_TT_TANGIBLE 19 #define LUA_TT_TANGIBLE 19
#define LUA_TT_TANGIBLEMETA 20 #define LUA_TT_TANGIBLEMETA 20
#define LUA_TT_DEADTANGIBLE 21 #define LUA_TT_GLOBALDB 21
#define LUA_TT_GLOBALDB 22 #define LUA_TT_CLASS 22
#define LUA_TT_CLASS 23
// World types enum. // World types enum.
@@ -458,8 +457,16 @@ public:
void makeclass(LuaSlot tab, LuaSlot name) const; void makeclass(LuaSlot tab, LuaSlot name) const;
void makeclass(LuaSlot tab, std::string_view 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 // Create a tangible, or look up an existing tangible.
// this module. // 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; int64_t tanid(LuaSlot tab) const;
// Return true if the value is a sortable key (string, number, or boolean). // Return true if the value is a sortable key (string, number, or boolean).

View File

@@ -78,6 +78,10 @@ public:
(*output_) << "<global-env>"; (*output_) << "<global-env>";
return; return;
} }
case LUA_TT_TANGIBLEMETA: {
(*output_) << "<tangible-metatable>";
return;
}
default: { default: {
(*output_) << "<unknown type #" << xtype << ">"; (*output_) << "<unknown type #" << xtype << ">";
return; return;
@@ -103,18 +107,16 @@ public:
// Determine the extended type of the object. If the // Determine the extended type of the object. If the
// expand flag is true, try to coerce it to a general table. // expand flag is true, try to coerce it to a general table.
int type = LS.xtype(value); int xtype = LS.xtype(value);
if (expand && (LS.istable(value))) {
type = LUA_TT_GENERAL;
}
// If it's anything but a general table, use 'atomic_print' // Print the atomic portion.
// and return. if (xtype != LUA_TT_GENERAL) {
if (type != LUA_TT_GENERAL) { atomic_print(xtype, value, true);
atomic_print(type, value, true); if ((xtype < LUA_TT_GENERAL) || (!expand)) {
LS.result(); LS.result();
return; return;
} }
}
// Find out whether it has a table number. If necessary, // Find out whether it has a table number. If necessary,
// assign one. // assign one.
@@ -189,7 +191,7 @@ public:
// Output the metatable. // Output the metatable.
LS.getmetatable(val, value); LS.getmetatable(val, value);
if (LS.istable(val)) { if (LS.istable(val) && (LS.gettabletype(val) != LUA_TT_TANGIBLEMETA)) {
multiline = true; multiline = true;
if (needcomma) (*output_) << ","; if (needcomma) (*output_) << ",";
needcomma = true; needcomma = true;

View File

@@ -26,7 +26,7 @@ LuaDefine(tangible_animstate, "tan",
LuaRet graphic, plane, x, y, z, facing; LuaRet graphic, plane, x, y, z, facing;
LuaStack LS(L, tanobj, graphic, plane, x, y, z, facing); LuaStack LS(L, tanobj, graphic, plane, x, y, z, facing);
World *w = World::fetch_global_pointer(L); 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(); const AnimStep &aqback = tan->anim_queue_.back();
LS.set(graphic, aqback.graphic()); LS.set(graphic, aqback.graphic());
LS.set(plane, aqback.plane()); LS.set(plane, aqback.plane());
@@ -44,7 +44,7 @@ LuaDefine(tangible_xyz, "tan",
LuaRet x, y, z; LuaRet x, y, z;
LuaStack LS(L, tanobj, x, y, z); LuaStack LS(L, tanobj, x, y, z);
World *w = World::fetch_global_pointer(L); 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(); const AnimStep &aqback = tan->anim_queue_.back();
LS.set(x, aqback.xyz().x); LS.set(x, aqback.xyz().x);
LS.set(y, aqback.xyz().y); LS.set(y, aqback.xyz().y);
@@ -60,7 +60,7 @@ LuaDefine(tangible_animate, "tan,configtable",
LuaStack LS(L, tanobj, config); LuaStack LS(L, tanobj, config);
LuaKeywordParser kp(LS, config); LuaKeywordParser kp(LS, config);
World *w = World::fetch_global_pointer(L); 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(); int64_t id = w->alloc_id_predictable();
AnimStep step; AnimStep step;
step.configure(kp, tan->anim_queue_.back()); step.configure(kp, tan->anim_queue_.back());
@@ -82,7 +82,7 @@ LuaDefine(tangible_setclass, "tan,class",
LuaVar classtab, mt; LuaVar classtab, mt;
LuaStack LS(L, tanobj, classname, classtab, mt); LuaStack LS(L, tanobj, classname, classtab, mt);
World *w = World::fetch_global_pointer(L); 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); eng::string err = LS.getclass(classtab, classname);
if (err != "") { if (err != "") {
luaL_error(L, "%s", err.c_str()); luaL_error(L, "%s", err.c_str());
@@ -101,7 +101,7 @@ LuaDefine(tangible_getclass, "tan",
LuaRet classname; LuaRet classname;
LuaStack LS(L, tanobj, mt, classtab, classname); LuaStack LS(L, tanobj, mt, classtab, classname);
World *w = World::fetch_global_pointer(L); World *w = World::fetch_global_pointer(L);
w->tangible_get(LS, tanobj); w->tangible_get(LS, tanobj, false);
LS.getmetatable(mt, tanobj); LS.getmetatable(mt, tanobj);
LS.rawget(classtab, mt, "__index"); LS.rawget(classtab, mt, "__index");
eng::string name = LS.classname(classtab); eng::string name = LS.classname(classtab);
@@ -120,8 +120,10 @@ LuaDefine(tangible_delete, "tan",
LuaArg tanobj; LuaArg tanobj;
LuaStack LS(L, tanobj); LuaStack LS(L, tanobj);
World *w = World::fetch_global_pointer(L); World *w = World::fetch_global_pointer(L);
Tangible *tan = w->tangible_get(LS, tanobj); Tangible *tan = w->tangible_get(LS, tanobj, true);
assert(tan != nullptr); // this should be checked above. if (tan == nullptr) {
return LS.result();
}
if (tan->is_an_actor()) { if (tan->is_an_actor()) {
luaL_error(L, "Cannot delete a player using tangible.delete, use tangible.redirect instead."); luaL_error(L, "Cannot delete a player using tangible.delete, use tangible.redirect instead.");
return 0; return 0;
@@ -205,14 +207,14 @@ LuaDefine(tangible_redirect, "tan1,tan2,bulldozetan1",
LuaStack LS(L, actor1, actor2, bldz); LuaStack LS(L, actor1, actor2, bldz);
World *w = World::fetch_global_pointer(L); World *w = World::fetch_global_pointer(L);
bool bulldoze = LS.ckboolean(bldz); 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()) { if (!tan1->is_an_actor()) {
luaL_error(L, "redirect source is not an actor"); luaL_error(L, "redirect source is not an actor");
} }
if (LS.isnil(actor2)) { if (LS.isnil(actor2)) {
w->redirects_[tan1->id()] = 0; w->redirects_[tan1->id()] = 0;
} else { } else {
Tangible *tan2 = w->tangible_get(LS, actor2); Tangible *tan2 = w->tangible_get(LS, actor2, false);
tan2->configure_id_pool_for_actor(); tan2->configure_id_pool_for_actor();
w->redirects_[tan1->id()] = tan2->id(); w->redirects_[tan1->id()] = tan2->id();
} }
@@ -268,7 +270,7 @@ LuaDefine(tangible_near, "tan,radius,omit_nowhere,omit_self",
LuaRet list; LuaRet list;
LuaStack LS(L, ltan, lradius, lomit_nowhere, lomit_self, list); LuaStack LS(L, ltan, lradius, lomit_nowhere, lomit_self, list);
World *w = World::fetch_global_pointer(L); 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(); const AnimStep &aqback = tan->anim_queue_.back();
PlaneScan scan; 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++) { for (int i = 1; ; i++) {
LS.rawget(place, tanlist, i); LS.rawget(place, tanlist, i);
if (LS.isnil(place)) break; if (LS.isnil(place)) break;
// Confirm that the place is a valid tangible, // Confirm that the place is a valid tangible,
// and get the tangible ID. // and get the tangible ID.
w->tangible_get(LS, place);
place_id = LS.tanid(place); place_id = LS.tanid(place);
// Get place's metatable and threads table. // Get place's metatable and threads table.

View File

@@ -134,14 +134,16 @@ World::TanVector World::tangible_get_all(const IdVector &ids) const {
return result; 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); int64_t id = LS.tanid(tab);
if (id == 0) { if (id == 0) {
luaL_error(LS.state(), "parameter is not a tangible"); luaL_error(LS.state(), "parameter is not a tangible");
} }
Tangible *result = tangible_get(id); Tangible *result = tangible_get(id);
if (!allowdel) {
if (result == nullptr) { if (result == nullptr) {
luaL_error(LS.state(), "parameter is not a tangible"); luaL_error(LS.state(), "argument is a deleted tangible, which is not allowed here");
}
} }
return result; return result;
} }
@@ -154,9 +156,9 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, const eng::string &plan
} }
assert(id != 0); assert(id != 0);
LuaVar tangibles, metatab; LuaVar metatab;
LuaRet database; LuaRet database;
LuaStack LS(L, tangibles, database, metatab); LuaStack LS(L, database, metatab);
// Create the C++ part of the structure. // Create the C++ part of the structure.
UniqueTangible &t = tangibles_[id]; UniqueTangible &t = tangibles_[id];
@@ -167,24 +169,13 @@ Tangible *World::tangible_make(lua_State *L, int64_t id, const eng::string &plan
t->anim_queue_.clear(plane); t->anim_queue_.clear(plane);
t->update_plane_item(); t->update_plane_item();
// Create the tangible's database and metatable. // Fetch the tangible's Lua database and metatable.
LS.set(database, LuaNewTable); LS.maketan(database, id);
LS.set(metatab, LuaNewTable); LS.getmetatable(metatab, database);
LS.setmetatable(database, metatab);
// Mark the tangible using the tabletype field. // Set up the inventory and thread table.
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(database, "inventory", LuaNewTable);
LS.rawset(metatab, "id", id);
LS.rawset(metatab, "threads", LuaNewTable); LS.rawset(metatab, "threads", LuaNewTable);
// LS.rawset(metatab, "__metatable", LuaNil);
LS.result(); LS.result();
if (!pushdb) lua_pop(L, 1); if (!pushdb) lua_pop(L, 1);
@@ -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) { void World::tangible_delete(int64_t id) {
lua_State *L = state(); lua_State *L = state();
LuaVar tangibles, database; LuaVar tangibles, database, metatab;
LuaStack LS(L, tangibles, database); LuaStack LS(L, tangibles, database, metatab);
// Fetch the C++ side of the tangible. // Fetch the C++ side of the tangible.
auto iter = tangibles_.find(id); auto iter = tangibles_.find(id);
@@ -204,16 +195,17 @@ void World::tangible_delete(int64_t id) {
} }
// Fetch the lua side of the tangible. // Fetch the lua side of the tangible.
LS.rawget(tangibles, LuaRegistry, "tangibles"); LS.maketan(database, id);
LS.rawget(database, tangibles, id);
assert(LS.istable(database)); assert(LS.istable(database));
LS.getmetatable(metatab, database);
// Clear out the database. // Clear out the database and the metatable.
LS.cleartable(database, true); LS.cleartable(database, false);
LS.settabletype(database, LUA_TT_DEADTANGIBLE); LS.cleartable(metatab, true);
// Remove the lua portion from the tangibles table. // Now put the bare minimum info back into the metatable.
LS.rawset(tangibles, id, LuaNil); LS.rawset(metatab, "id", id);
LS.rawset(metatab, "__metatable", false);
// Remove the C++ portion from the tangibles table. // Remove the C++ portion from the tangibles table.
tangibles_.erase(iter); tangibles_.erase(iter);

View File

@@ -304,12 +304,7 @@ static void set_transmitted_value(LuaStack &LS, LuaSlot tangibles, LuaSlot ntmap
case LUA_TT_TANGIBLE: { case LUA_TT_TANGIBLE: {
int64_t id = sb->read_int64(); int64_t id = sb->read_int64();
DebugLine(dbc) << dbinfo << "tan " << id; DebugLine(dbc) << dbinfo << "tan " << id;
LS.rawget(target, tangibles, id); LS.maketan(target, 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());
}
return; return;
} }
case LUA_TT_GLOBALENV: { case LUA_TT_GLOBALENV: {

View File

@@ -1,6 +1,7 @@
#include "world.hpp"
#include <iostream> #include <iostream>
#include "world.hpp"
util::IdVector World::get_visible_union(int64_t actor_id, World *master) { util::IdVector World::get_visible_union(int64_t actor_id, World *master) {
return util::sort_union_id_vectors( return util::sort_union_id_vectors(
master->get_near(actor_id, RadiusVisibility, true, false, false), master->get_near(actor_id, RadiusVisibility, true, false, false),
@@ -85,11 +86,13 @@ 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; StreamBuffer tsb;
// Get the specified tangibles in both models. // 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 mvis = master->tangible_get_all(visible);
TanVector svis = tangible_get_all(visible); TanVector svis = tangible_get_all(visible);
assert(mvis.size() == svis.size()); assert(mvis.size() == svis.size());
@@ -169,11 +172,13 @@ void World::patch_luatabs(StreamBuffer *sb, DebugCollector *dbc) {
int64_t actor_id = sb->read_int64(); int64_t actor_id = sb->read_int64();
util::HashValue closehash = sb->read_hashvalue(); util::HashValue closehash = sb->read_hashvalue();
int ncreate = sb->read_int32(); 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)); assert(closehash == util::hash_id_vector(closetans));
number_lua_tables(closetans); number_lua_tables(closetans);
create_new_tables(ncreate); 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_tangible_databases(sb, dbc);
patch_numbered_tables(sb, dbc); patch_numbered_tables(sb, dbc);
unnumber_lua_tables(); unnumber_lua_tables();
@@ -183,7 +188,8 @@ void World::diff_luatabs(int64_t actor_id, World *master, StreamBuffer *xsb) {
StreamBuffer tsb; StreamBuffer tsb;
// Calculate the set of close tangibles. // 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); assert(get_near(actor_id, RadiusClose, true, false, true) == closetans);
util::HashValue closehash = util::hash_id_vector(closetans); util::HashValue closehash = util::hash_id_vector(closetans);
@@ -249,8 +255,10 @@ void World::diff_tanclass(int64_t actor_id, World *master, StreamBuffer *xsb) {
MLS.rawget(mtangibles, LuaRegistry, "tangibles"); MLS.rawget(mtangibles, LuaRegistry, "tangibles");
// Calculate the set of close tangibles. // Calculate the set of close tangibles.
// TODO: we've already calculated this in an earlier function. This is wasteful. // TODO: we've already calculated this in an earlier function. This is
util::IdVector closetans = master->get_near(actor_id, RadiusClose, true, false, true); // wasteful.
util::IdVector closetans =
master->get_near(actor_id, RadiusClose, true, false, true);
tsb.write_int32(0); tsb.write_int32(0);
int write_count_after = tsb.total_writes(); int write_count_after = tsb.total_writes();

View File

@@ -55,14 +55,11 @@ eng::string World::tangible_pprint(int64_t id) const {
LS.rawget(tan, tangibles, id); LS.rawget(tan, tangibles, id);
eng::ostringstream oss; eng::ostringstream oss;
if (LS.istable(tan)) { if (LS.istable(tan)) {
LS.getmetatable(meta, tan);
LS.clearmetatable(tan);
PrettyPrintOptions opts; PrettyPrintOptions opts;
opts.indent = false; opts.indent = false;
pprint(LS, tan, opts, &oss); pprint(LS, tan, opts, &oss);
LS.setmetatable(tan, meta);
} else { } else {
oss << "<no such tangible: " << id << ">"; oss << "<no such tangible " << id << ">{}";
} }
LS.result(); LS.result();
return oss.str(); return oss.str();
@@ -384,7 +381,7 @@ LuaDefine(unittests_world3diffluatab, "", "some unit tests") {
// The data in the master model should now look like this: // The data in the master model should now look like this:
const char *expect_123 = const char *expect_123 =
"{ " "<tangible 123>{ "
"bacon='crispy', " "bacon='crispy', "
"inventory={ gold='wealthy' }, " "inventory={ gold='wealthy' }, "
"skills={ " "skills={ "
@@ -393,7 +390,7 @@ LuaDefine(unittests_world3diffluatab, "", "some unit tests") {
"} " "} "
"}"; "}";
const char *expect_345 = const char *expect_345 =
"{ " "<tangible 345>{ "
"inventory={ gold='poor' }, " "inventory={ gold='poor' }, "
"phone='867-5309' " "phone='867-5309' "
"}"; "}";

View File

@@ -137,17 +137,21 @@ public:
// Get a pointer to the specified tangible. // 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); Tangible *tangible_get(int64_t id);
const Tangible *tangible_get(int64_t id) const; const Tangible *tangible_get(int64_t id) const;
// Get a pointer to the specified tangible. // Get a pointer to the specified tangible.
// //
// The value on the lua stack should be a valid lua tangible. If not, // The value on the lua stack should be a lua tangible.
// a lua error is generated.
// //
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. // Get pointers to many tangibles.
// //