Files
integration/luprex/cpp/core/world-testing.cpp
2023-04-14 15:13:59 -04:00

502 lines
18 KiB
C++

#include "world.hpp"
#include "pprint.hpp"
#include "json.hpp"
#include <cassert>
void World::tangible_walkto(int64_t id, int64_t animid, float x, float y) {
Tangible *t = tangible_get(id);
assert(animid != 0);
assert(t != nullptr);
AnimStep step;
step.set_action("walkto");
step.set_x(x);
step.set_y(y);
t->anim_queue_.add(animid, step);
}
eng::string World::tangible_anim_debug_string(int64_t id) const {
const Tangible *t = tangible_get(id);
if (t == 0) return "no such tangible";
return t->anim_queue_.steps_debug_string();
}
eng::string World::tangible_id_pool_debug_string(int64_t id) const {
const Tangible *t = tangible_get(id);
if (t == 0) return "no such tangible";
return t->id_player_pool_.debug_string();
}
eng::string World::tangible_ids_debug_string() const {
util::IdVector idv;
for (const auto &pair : tangibles_) {
idv.push_back(pair.first);
}
std::sort(idv.begin(), idv.end());
return util::id_vector_debug_string(idv);
}
eng::string World::tangibles_near_debug_string(int64_t actor, int64_t distance) {
eng::ostringstream result;
for (int64_t id : get_near(actor, distance, true, false, true)) {
const Tangible *tan = tangible_get(id);
const AnimStep &aqback = tan->anim_queue_.back();
result << id << ": " << aqback.graphic() << " " << aqback.plane() << " " << aqback.xyz().debug_string() << std::endl;
}
return result.str();
}
eng::string World::tangible_pprint(int64_t id) const {
lua_State *L = state();
LuaVar tangibles, tan, meta;
LuaExtStack LS(L, tangibles, tan, meta);
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(tan, tangibles, id);
eng::ostringstream oss;
if (LS.istable(tan)) {
PrettyPrintOptions opts;
opts.indent = false;
pprint(LS, tan, opts, &oss);
} else {
oss << "<no such tangible " << id << ">{}";
}
return oss.str();
}
eng::string World::numbered_tables_debug_string() const {
lua_State *L = state();
LuaVar ntmap, tab, tid;
LuaExtStack LS(L, ntmap, tab, tid);
eng::vector<eng::string> result;
eng::ostringstream oss;
// Fetch the numbered tables map.
LS.rawget(ntmap, LuaRegistry, "ntmap");
// Iterate over the map. For each table, if it has
// a TID, output that. Otherwise, output "unknown".
for (int i = 1; i < 10000; i++) {
LS.rawget(tab, ntmap, i);
if (!LS.istable(tab)) break;
LS.rawget(tid, tab, "TID");
if (LS.isstring(tid)) {
result.push_back(LS.ckstring(tid));
} else {
result.push_back("unknown");
}
}
std::sort(result.begin(), result.end());
for (const eng::string &s : result) {
oss << s << ";";
}
return oss.str();
}
eng::string World::paired_tables_debug_string(lua_State *master) const {
lua_State *synch = state();
LuaVar mntmap, sntmap, mtab, stab, mtid, stid;
LuaExtStack MLS(master, mntmap, mtab, mtid);
LuaExtStack SLS(synch, sntmap, stab, stid);
eng::vector<std::pair<eng::string, eng::string>> result;
eng::ostringstream oss;
// Fetch the numbered tables map.
MLS.rawget(mntmap, LuaRegistry, "ntmap");
SLS.rawget(sntmap, LuaRegistry, "ntmap");
int m_ntables = MLS.rawlen(mntmap);
int s_ntables = MLS.rawlen(sntmap);
assert(m_ntables == s_ntables);
for (int i = 1; i <= m_ntables; i++) {
MLS.rawget(mtab, mntmap, i);
SLS.rawget(stab, sntmap, i);
if (MLS.istable(mtab) && SLS.istable(stab)) {
eng::string mname = "unknown";
eng::string sname = "unknown";
MLS.rawget(mtid, mtab, "TID");
if (MLS.isstring(mtid)) {
mname = MLS.ckstring(mtid);
}
SLS.rawget(stid, stab, "TID");
if (SLS.isstring(stid)) {
sname = SLS.ckstring(stid);
}
result.push_back(std::make_pair(mname, sname));
}
}
std::sort(result.begin(), result.end());
for (const auto &pair : result) {
oss << pair.first << "=" << pair.second << ";";
}
return oss.str();
}
void World::tangible_set_string(int64_t id, const eng::string &path, const eng::string &value) {
lua_State *L = state();
LuaVar tangibles, tab, subtab;
LuaExtStack LS(L, tangibles, tab, subtab);
// Fetch the lua side of the tangible.
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(tab, tangibles, id);
assert(LS.istable(tab));
// Split the path parts into the table names and final part.
util::StringVec pathparts = util::split(path, '.');
assert(pathparts.size() >= 1);
eng::string finalpart = pathparts.back();
pathparts.pop_back();
// Create subtables as necessary.
for (const eng::string &subname : pathparts) {
LS.rawget(subtab, tab, subname);
if (LS.isnil(subtab)) {
LS.set(subtab, LuaNewTable);
LS.rawset(tab, subname, subtab);
}
assert(LS.istable(subtab));
LS.set(tab, subtab);
}
// Set the string value.
LS.rawset(tab, finalpart, value);
}
void World::tangible_copy_global(int64_t id, const eng::string &path, const eng::string &global) {
lua_State *L = state();
LuaVar tangibles, tab, subtab, globtab, value;
LuaExtStack LS(L, tangibles, tab, subtab, globtab, value);
// Fetch the lua side of the tangible.
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(tab, tangibles, id);
assert(LS.istable(tab));
// Split the path parts into the table names and final part.
util::StringVec pathparts = util::split(path, '.');
assert(pathparts.size() >= 1);
eng::string finalpart = pathparts.back();
pathparts.pop_back();
// Create subtables as necessary.
for (const eng::string &subname : pathparts) {
LS.rawget(subtab, tab, subname);
if (LS.isnil(subtab)) {
LS.set(subtab, LuaNewTable);
LS.rawset(tab, subname, subtab);
}
assert(LS.istable(subtab));
LS.set(tab, subtab);
}
// Copy the global value.
LS.getglobaltable(globtab);
LS.rawget(value, globtab, global);
LS.rawset(tab, finalpart, value);
}
void World::tangible_set_class(int64_t id, const eng::string &c) const {
LuaVar tangibles, tan, meta, sclass;
LuaExtStack LS(state(), tangibles, tan, meta, sclass);
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(tan, tangibles, id);
assert(LS.istable(tan));
LS.getmetatable(meta, tan);
if (c == "") {
LS.set(sclass, LuaNil);
} else {
LS.makeclass(sclass, c);
}
LS.rawset(meta, "__index", sclass);
}
void World::set_global_json(const eng::string &gvar, const eng::string &json) {
LuaVar decoded;
LuaExtStack LS(state(), decoded);
bool ok = json::decode(LS, decoded, json);
if (!ok) {
luaL_error(state(), "invalid json");
return;
}
set_global(LS, gvar, decoded);
}
eng::string World::get_global_json(const eng::string &gvar) {
LuaVar value, globaldb;
LuaExtStack LS(state(), globaldb, value);
LS.rawget(globaldb, LuaRegistry, "globaldb");
LS.rawget(value, globaldb, gvar);
eng::string out;
eng::string error = json::encode(LS, value, out, false, 10000);
if (!error.empty()) {
luaL_error(state(), "%s", error.c_str());
return "";
}
return out;
}
eng::string World::tangible_get_class(int64_t id) const {
LuaVar tangibles, tan, meta, sclass;
LuaExtStack LS(state(), tangibles, tan, meta, sclass);
LS.rawget(tangibles, LuaRegistry, "tangibles");
LS.rawget(tan, tangibles, id);
assert(LS.istable(tan));
LS.getmetatable(meta, tan);
LS.rawget(sclass, meta, "__index");
eng::string result = LS.classname(sclass);
return result;
}
static bool worlds_identical(const UniqueWorld &w1, const UniqueWorld &w2) {
StreamBuffer sbw1, sbw2;
w1->serialize(&sbw1);
w2->serialize(&sbw2);
return sbw1.contents_equal(&sbw2);
}
LuaDefine(unittests_world1animdiff, "", "some unit tests") {
UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb;
util::IdVector ids = util::id_vector_create(123, 345);
// Create some tangibles, and add some animations.
m->tangible_make(0, 123, "somewhere", false);
m->tangible_make(0, 345, "somewhere", false);
m->tangible_walkto(123, 770, 3, 4);
m->tangible_walkto(345, 771, 6, 2);
LuaAssertStrEq(L, m->tangible_ids_debug_string(), "123,345");
LuaAssertStrEq(L, m->tangible_anim_debug_string(123),
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
"id=770 action=walkto x=3 y=4; ");
LuaAssertStrEq(L, m->tangible_anim_debug_string(345),
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
"id=771 action=walkto x=6 y=2; ");
// Now difference transmit all that to the client.
ss->diff_visible(ids, m.get(), &sb);
cs->patch_visible(&sb, nullptr);
LuaAssertStrEq(L, ss->tangible_ids_debug_string(), "123,345");
LuaAssertStrEq(L, ss->tangible_anim_debug_string(123),
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
"id=770 action=walkto x=3 y=4; ");
LuaAssertStrEq(L, ss->tangible_anim_debug_string(345),
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
"id=771 action=walkto x=6 y=2; ");
LuaAssert(L, worlds_identical(ss, cs));
// Now add some more animation records to the master.
m->tangible_walkto(123, 772, 7, 3);
m->tangible_walkto(345, 773, 2, 5);
LuaAssertStrEq(L, m->tangible_anim_debug_string(123),
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
"id=770 action=walkto x=3 y=4; "
"id=772 action=walkto x=7 y=3; ");
LuaAssertStrEq(L, m->tangible_anim_debug_string(345),
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
"id=771 action=walkto x=6 y=2; "
"id=773 action=walkto x=2 y=5; ");
// Now difference transmit all that to the client again.
ss->diff_visible(ids, m.get(), &sb);
cs->patch_visible(&sb, nullptr);
LuaAssertStrEq(L, ss->tangible_anim_debug_string(123),
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
"id=770 action=walkto x=3 y=4; "
"id=772 action=walkto x=7 y=3; ");
LuaAssertStrEq(L, ss->tangible_anim_debug_string(345),
"id=0 action= plane=somewhere x=0 y=0 z=0 facing=0 graphic=; "
"id=771 action=walkto x=6 y=2; "
"id=773 action=walkto x=2 y=5; ");
LuaAssert(L, worlds_identical(ss, cs));
// Delete tangible 345.
m->tangible_delete(345);
LuaAssertStrEq(L, m->tangible_ids_debug_string(), "123");
// And difference transmit
ss->diff_visible(ids, m.get(), &sb);
cs->patch_visible(&sb, nullptr);
LuaAssertStrEq(L, ss->tangible_ids_debug_string(), "123");
LuaAssert(L, worlds_identical(ss, cs));
return 0;
}
LuaDefine(unittests_world2pairtab, "", "some unit tests") {
UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb;
int ncreate;
// Create a master model containing some general tables, and
// some specialty tables (not numberable).
m->tangible_make(0, 123, "somewhere", false);
m->tangible_set_string(123, "inventory.TID", "inventory");
m->tangible_set_string(123, "transactions.TID", "transactions");
m->tangible_set_string(123, "skills.TID", "skills");
m->tangible_set_string(123, "skills.leet.TID", "skills.leet");
m->tangible_set_string(123, "inventory.cplx.TID", "inventory.cplx");
m->tangible_copy_global(123, "math", "math");
m->tangible_copy_global(123, "gltab", "_G");
// Now we're going to create a synchronous model that's similar to, but not
// exactly the same as that master model.
ss->tangible_make(0, 123, "somewhere", false);
ss->tangible_set_string(123, "inventory.TID", "inventory");
ss->tangible_set_string(123, "skills.TID", "skills");
ss->tangible_set_string(123, "skills.crap.TID", "skills.crap");
ss->tangible_set_string(123, "skills.leet.TID", "skills.leet");
ss->tangible_set_string(123, "math.TID", "math");
ss->tangible_set_string(123, "gltab.TID", "gltab");
// Now we're going to test the numbering and pairing of tables.
// Only these tables should pair: inventory, skills, and skills.leet
ss->number_lua_tables(util::id_vector_create(123));
LuaAssertStrEq(L, ss->numbered_tables_debug_string(),
"gltab;inventory;math;skills;skills.crap;skills.leet;");
ss->pair_lua_tables(util::id_vector_create(123), m->state());
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state()),
"inventory=inventory;skills=skills;skills.leet=skills.leet;");
// Test the creation of new tables during difference transmission.
// The master world model above has two tables that couldn't be paired
// to the client: inventory.cplx, and transactions. These two tables
// should be paired to new, created tables.
ncreate = m->number_remaining_tables(util::id_vector_create(123), m->state());
LuaAssert(L, ncreate == 2);
ss->create_new_tables(ncreate);
LuaAssertStrEq(L, ss->paired_tables_debug_string(m->state()),
"inventory=inventory;inventory.cplx=unknown;skills=skills;skills.leet=skills.leet;transactions=unknown;");
return 0;
}
LuaDefine(unittests_world3diffluatab, "", "some unit tests") {
UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb;
// Initialize all three models so that a tangible exists.
m->tangible_make(0, 123, "somewhere", false);
ss->tangible_make(0, 123, "somewhere", false);
cs->tangible_make(0, 123, "somewhere", false);
m->tangible_make(0, 345, "somewhere", false);
ss->tangible_make(0, 345, "somewhere", false);
cs->tangible_make(0, 345, "somewhere", false);
// Put some data into the master model.
m->tangible_set_string(123, "bacon", "crispy");
m->tangible_set_string(123, "inventory.gold", "wealthy");
m->tangible_set_string(123, "skills.hunting", "leet");
m->tangible_set_string(123, "skills.magic.fireball", "weak");
m->tangible_set_string(345, "inventory.gold", "poor");
m->tangible_set_string(345, "phone", "867-5309");
// The data in the master model should now look like this:
const char *expect_123 =
"<tangible 123>{ "
"bacon='crispy', "
"inventory={ gold='wealthy' }, "
"skills={ "
"hunting='leet', "
"magic={ fireball='weak' } "
"} "
"}";
const char *expect_345 =
"<tangible 345>{ "
"inventory={ gold='poor' }, "
"phone='867-5309' "
"}";
LuaAssertStrEq(L, m->tangible_pprint(123), expect_123);
LuaAssertStrEq(L, m->tangible_pprint(345), expect_345);
// Difference transmit.
ss->diff_luatabs(123, m.get(), &sb);
cs->patch_luatabs(&sb, nullptr);
// Verify that the data was transmitted.
LuaAssertStrEq(L, ss->tangible_pprint(123), expect_123);
LuaAssertStrEq(L, cs->tangible_pprint(123), expect_123);
LuaAssertStrEq(L, ss->tangible_pprint(345), expect_345);
LuaAssertStrEq(L, cs->tangible_pprint(345), expect_345);
LuaAssert(L, worlds_identical(ss, cs));
return 0;
}
LuaDefine(unittests_world4difftanclass, "", "some unit tests") {
UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb;
// Initialize all three models so that a tangible exists.
m->tangible_make(0, 123, "somewhere", false);
ss->tangible_make(0, 123, "somewhere", false);
cs->tangible_make(0, 123, "somewhere", false);
// Change the lua class of the tangible.
m->tangible_set_class(123, "chicken");
LuaAssertStrEq(L, m->tangible_get_class(123), "chicken");
// Difference transmit.
ss->diff_tanclass(123, m.get(), &sb);
cs->patch_tanclass(&sb, nullptr);
// Verify that the data was transmitted.
LuaAssertStrEq(L, ss->tangible_get_class(123), "chicken");
LuaAssertStrEq(L, cs->tangible_get_class(123), "chicken");
LuaAssert(L, worlds_identical(ss, cs));
// Change the class again.
m->tangible_set_class(123, "");
LuaAssertStrEq(L, m->tangible_get_class(123), "");
// Difference transmit.
ss->diff_tanclass(123, m.get(), &sb);
cs->patch_tanclass(&sb, nullptr);
// Verify that the data was transmitted.
LuaAssertStrEq(L, ss->tangible_get_class(123), "");
LuaAssertStrEq(L, cs->tangible_get_class(123), "");
LuaAssert(L, worlds_identical(ss, cs));
return 0;
}
LuaDefine(unittests_world5diffglobals, "", "some unit tests") {
UniqueWorld m(new World(WORLD_TYPE_MASTER));
UniqueWorld ss(new World(WORLD_TYPE_PREDICTIVE));
UniqueWorld cs(new World(WORLD_TYPE_PREDICTIVE));
StreamBuffer sb;
m->set_global_json("x", "3");
ss->diff_globals(m.get(), &sb);
cs->patch_globals(&sb, nullptr);
LuaAssertStrEq(L, cs->get_global_json("x"), "3");
m->set_global_json("x", "4");
m->set_global_json("x", "5");
m->set_global_json("y", "6");
ss->diff_globals(m.get(), &sb);
cs->patch_globals(&sb, nullptr);
LuaAssertStrEq(L, cs->get_global_json("x"), "5");
LuaAssertStrEq(L, cs->get_global_json("y"), "6");
cs->set_global_json("x", "2");
ss->set_global_json("x", "2");
ss->diff_globals(m.get(), &sb);
cs->patch_globals(&sb, nullptr);
LuaAssertStrEq(L, cs->get_global_json("x"), "5");
return 0;
}