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);
}
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;

View File

@@ -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).

View File

@@ -78,6 +78,10 @@ public:
(*output_) << "<global-env>";
return;
}
case LUA_TT_TANGIBLEMETA: {
(*output_) << "<tangible-metatable>";
return;
}
default: {
(*output_) << "<unknown type #" << xtype << ">";
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;

View File

@@ -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.

View File

@@ -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);

View File

@@ -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: {

View File

@@ -1,10 +1,11 @@
#include "world.hpp"
#include <iostream>
#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);

View File

@@ -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 << "<no such tangible: " << id << ">";
oss << "<no such tangible " << id << ">{}";
}
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 =
"{ "
"<tangible 123>{ "
"bacon='crispy', "
"inventory={ gold='wealthy' }, "
"skills={ "
@@ -393,7 +390,7 @@ LuaDefine(unittests_world3diffluatab, "", "some unit tests") {
"} "
"}";
const char *expect_345 =
"{ "
"<tangible 345>{ "
"inventory={ gold='poor' }, "
"phone='867-5309' "
"}";

View File

@@ -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.
//