Working on unit tests for class world

This commit is contained in:
2021-08-03 11:25:12 -04:00
parent 91d7e1c15d
commit 202c5a24ba
13 changed files with 240 additions and 103 deletions

View File

@@ -425,7 +425,7 @@ std::string AnimQueue::debug_string() const {
return oss.str(); return oss.str();
} }
void AnimQueue::serialize_size_and_steps(StreamBuffer *sb) const { void AnimQueue::serialize(StreamBuffer *sb) const {
assert(valid()); // can't serialize an invalid animqueue. assert(valid()); // can't serialize an invalid animqueue.
sb->write_uint8(size_limit_); sb->write_uint8(size_limit_);
sb->write_uint8(steps_.size()); sb->write_uint8(steps_.size());
@@ -434,12 +434,7 @@ void AnimQueue::serialize_size_and_steps(StreamBuffer *sb) const {
} }
} }
void AnimQueue::serialize(StreamBuffer *sb) const { void AnimQueue::deserialize(StreamBuffer *sb) {
serialize_size_and_steps(sb);
sb->write_int64(version_number_);
}
void AnimQueue::deserialize_size_and_steps(StreamBuffer *sb) {
size_limit_ = sb->read_uint8(); size_limit_ = sb->read_uint8();
size_t nsteps = sb->read_uint8(); size_t nsteps = sb->read_uint8();
steps_.resize(nsteps); steps_.resize(nsteps);
@@ -448,11 +443,7 @@ void AnimQueue::deserialize_size_and_steps(StreamBuffer *sb) {
step.read_from(sb); step.read_from(sb);
if (i > 0) step.echo(steps_[i - 1]); if (i > 0) step.echo(steps_[i - 1]);
} }
} version_number_ = 0;
void AnimQueue::deserialize(StreamBuffer *sb) {
deserialize_size_and_steps(sb);
version_number_ = sb->read_int64();
} }
bool AnimQueue::need_patch(const AnimQueue &auth) const { bool AnimQueue::need_patch(const AnimQueue &auth) const {
@@ -624,7 +615,7 @@ LuaDefine(unittests_animqueue, "c") {
aqds.deserialize(&sb); aqds.deserialize(&sb);
LuaAssertStrEq(L, aqds.debug_string(), LuaAssertStrEq(L, aqds.debug_string(),
"version=4; limit=5; " "version=0; limit=5; "
"id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=; " "id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=; "
"id=12345 action=walk x=3 y=4 z=5; " "id=12345 action=walk x=3 y=4 z=5; "
"id=12346 action=setgraphic graphic=banana; " "id=12346 action=setgraphic graphic=banana; "

View File

@@ -182,8 +182,10 @@ public:
void add(int64_t id, const AnimStep &step); void add(int64_t id, const AnimStep &step);
// Serialize or deserialize to a StreamBuffer // Serialize or deserialize to a StreamBuffer
void serialize_size_and_steps(StreamBuffer *sb) const; //
void deserialize_size_and_steps(StreamBuffer *sb); // Caution: version numbers are not stored. On deserialize,
// version number is set to zero.
//
void serialize(StreamBuffer *sb) const; void serialize(StreamBuffer *sb) const;
void deserialize(StreamBuffer *sb); void deserialize(StreamBuffer *sb);
@@ -196,11 +198,21 @@ public:
// Get the final resting place after all animations are complete. // Get the final resting place after all animations are complete.
const AnimStep &back() const; const AnimStep &back() const;
// (For testing): change the size limit. public:
////////////////////////////////////////////////////////////////////////////
//
// TESTING SUPPORT
//
// The following functions are not designed to be useful for production
// code, they're designed to be helpful for unit testing.
//
////////////////////////////////////////////////////////////////////////////
// Change the size limit.
void full_clear_and_set_limit(int szl); void full_clear_and_set_limit(int szl);
void set_limit(int szl); void set_limit(int szl);
// (For testing): make sure the invariants are preserved. // Make sure the invariants are preserved.
bool valid() const; bool valid() const;
// Convert to a string for debugging purposes. // Convert to a string for debugging purposes.

View File

@@ -37,7 +37,7 @@
// //
// THE NUMERIC RANGES // THE NUMERIC RANGES
// //
// * 0x0000+ : reserved for future expansion. // * 0x0000+ : reserved for manually-created tangible IDs.
// * 0x0001+ : used by master model's IdGlobalPool to create batches. // * 0x0001+ : used by master model's IdGlobalPool to create batches.
// * 0x0010+ : used by master model's IdGlobalPool to create individual IDs. // * 0x0010+ : used by master model's IdGlobalPool to create individual IDs.
// * 0x001E+ : used by sync model's IdGlobalPool to create individual IDs. // * 0x001E+ : used by sync model's IdGlobalPool to create individual IDs.

View File

@@ -69,7 +69,7 @@ void LuaSnap::serialize(StreamBuffer *sb) {
int64_t pos2 = sb->total_writes(); int64_t pos2 = sb->total_writes();
sb->overwrite_int64(pos1, pos2 - pos1); sb->overwrite_int64(pos1, pos2 - pos1);
lua_settop(state_, 0); lua_settop(state_, 0);
std::cerr << "Eris dump is " << pos2-pos1 << " bytes." << std::endl; // std::cerr << "Eris dump is " << pos2-pos1 << " bytes." << std::endl;
} }
void LuaSnap::deserialize(StreamBuffer *sb) { void LuaSnap::deserialize(StreamBuffer *sb) {

View File

@@ -12,7 +12,7 @@
#define CELL_SCALE 10.0 #define CELL_SCALE 10.0
// Round a float and return as integer. Clamp result to the specified range. // Round a float and return as integer. Clamp result to the specified range.
static int round_and_clamp(double x, int lo, int hi) { static int round_and_clamp(float x, int lo, int hi) {
x = round(x); x = round(x);
if (x < lo) return lo; if (x < lo) return lo;
if (x > hi) return hi; if (x > hi) return hi;
@@ -37,7 +37,7 @@ struct CellRange {
// beyond the maximum cell range. In that case, it clamps to the edge // beyond the maximum cell range. In that case, it clamps to the edge
// of the cell range. // of the cell range.
// //
static CellRange rect_cell_range(double x1, double y1, double x2, double y2) { static CellRange rect_cell_range(float x1, float y1, float x2, float y2) {
CellRange result; CellRange result;
result.xlo = round_and_clamp(x1 / CELL_SCALE, -CELL_LIMIT, CELL_LIMIT); result.xlo = round_and_clamp(x1 / CELL_SCALE, -CELL_LIMIT, CELL_LIMIT);
result.ylo = round_and_clamp(y1 / CELL_SCALE, -CELL_LIMIT, CELL_LIMIT); result.ylo = round_and_clamp(y1 / CELL_SCALE, -CELL_LIMIT, CELL_LIMIT);
@@ -53,9 +53,9 @@ static int64_t cell_id(int64_t cellx, int64_t celly) {
} }
// Get the cell ID of the specified point // Get the cell ID of the specified point
static int64_t point_cell_id(double x, double y) { static int64_t point_cell_id(float x, float y) {
double cellx = round_and_clamp(x / CELL_SCALE, -CELL_LIMIT, CELL_LIMIT); float cellx = round_and_clamp(x / CELL_SCALE, -CELL_LIMIT, CELL_LIMIT);
double celly = round_and_clamp(y / CELL_SCALE, -CELL_LIMIT, CELL_LIMIT); float celly = round_and_clamp(y / CELL_SCALE, -CELL_LIMIT, CELL_LIMIT);
return cell_id(int64_t(cellx), int64_t(celly)); return cell_id(int64_t(cellx), int64_t(celly));
} }
@@ -85,7 +85,7 @@ void PlaneMap::insert(const std::string &plane, int64_t cellid, PlaneItem *clien
l.push_back(client); l.push_back(client);
} }
void PlaneItem::set_pos(const std::string &plane, double x, double y, double z) { void PlaneItem::set_pos(const std::string &plane, float x, float y, float z) {
int64_t old_cell = point_cell_id(x_, y_); int64_t old_cell = point_cell_id(x_, y_);
int64_t new_cell = point_cell_id(x, y); int64_t new_cell = point_cell_id(x, y);
@@ -160,7 +160,7 @@ int PlaneMap::total_cells() const {
return total; return total;
} }
PlaneMap::IdVector PlaneMap::scan_radius(const std::string &plane, double x, double y, double radius, int64_t prepend) const { PlaneMap::IdVector PlaneMap::scan_radius(const std::string &plane, float x, float y, float radius, int64_t prepend) const {
PlaneMap::IdVector result; PlaneMap::IdVector result;
if (prepend != 0) { if (prepend != 0) {
result.push_back(prepend); result.push_back(prepend);
@@ -169,7 +169,7 @@ PlaneMap::IdVector PlaneMap::scan_radius(const std::string &plane, double x, dou
if (piter != planes_.end()) { if (piter != planes_.end()) {
const Plane &p = piter->second; const Plane &p = piter->second;
CellRange range = rect_cell_range(x - radius, y - radius, x + radius, y + radius); CellRange range = rect_cell_range(x - radius, y - radius, x + radius, y + radius);
double radsq = radius*radius; float radsq = radius*radius;
for (int cy = range.ylo; cy <= range.yhi; cy++) { for (int cy = range.ylo; cy <= range.yhi; cy++) {
for (int cx = range.xlo; cx <= range.xhi; cx++) { for (int cx = range.xlo; cx <= range.xhi; cx++) {
auto liter = p.find(cell_id(cx, cy)); auto liter = p.find(cell_id(cx, cy));
@@ -189,8 +189,8 @@ PlaneMap::IdVector PlaneMap::scan_radius(const std::string &plane, double x, dou
} }
LuaDefine(unittests_planemap, "c") { LuaDefine(unittests_planemap, "c") {
double SC = CELL_SCALE; float SC = CELL_SCALE;
double E = CELL_SCALE * 0.4; float E = CELL_SCALE * 0.4;
int LO = -CELL_LIMIT; int LO = -CELL_LIMIT;
int HI = CELL_LIMIT; int HI = CELL_LIMIT;
PlaneMap pm; PlaneMap pm;

View File

@@ -86,7 +86,7 @@ class PlaneItem {
private: private:
PlaneMap *pmap_; PlaneMap *pmap_;
std::string plane_; std::string plane_;
double x_, y_, z_; float x_, y_, z_;
int64_t id_; int64_t id_;
public: public:
@@ -98,15 +98,15 @@ public:
int64_t id() const { return id_; } int64_t id() const { return id_; }
const std::string &plane() const { return plane_; } const std::string &plane() const { return plane_; }
const double x() const { return x_; } const float x() const { return x_; }
const double y() const { return y_; } const float y() const { return y_; }
const double z() const { return z_; } const float z() const { return z_; }
void untrack(); void untrack();
void track(PlaneMap *pmap); void track(PlaneMap *pmap);
void set_pos(const std::string &plane, double x, double y, double z); void set_pos(const std::string &plane, float x, float y, float z);
void set_xyz(double x, double y, double z) { set_pos(plane_, x, y, z); } void set_xyz(float x, float y, float z) { set_pos(plane_, x, y, z); }
}; };
class PlaneMap { class PlaneMap {
@@ -124,7 +124,7 @@ public:
~PlaneMap(); ~PlaneMap();
// Caution: scan_radius is not deterministically ordered. // Caution: scan_radius is not deterministically ordered.
IdVector scan_radius(const std::string &plane, double x, double y, double radius, int64_t prepend) const; IdVector scan_radius(const std::string &plane, float x, float y, float radius, int64_t prepend) const;
private: private:
// unit testing stuff. // unit testing stuff.

View File

@@ -41,6 +41,10 @@ int64_t StreamBuffer::total_writes() const {
return (write_cursor_ - buf_lo_) + pre_read_count_; return (write_cursor_ - buf_lo_) + pre_read_count_;
} }
int64_t StreamBuffer::fill() const {
return write_cursor_ - read_cursor_;
}
bool StreamBuffer::layout_is(int64_t a, int64_t b, int64_t c) { bool StreamBuffer::layout_is(int64_t a, int64_t b, int64_t c) {
if (read_cursor_ - buf_lo_ != a) return false; if (read_cursor_ - buf_lo_ != a) return false;
if (write_cursor_ - read_cursor_ != b) return false; if (write_cursor_ - read_cursor_ != b) return false;
@@ -400,6 +404,14 @@ void StreamBuffer::copy_into(StreamBuffer *sb) {
sb->write_bytes(read_cursor_, write_cursor_ - read_cursor_); sb->write_bytes(read_cursor_, write_cursor_ - read_cursor_);
} }
bool StreamBuffer::contents_equal(const StreamBuffer *other) const {
int64_t len = fill();
if (len != other->fill()) {
return false;
}
return memcmp(read_cursor_, other->read_cursor_, len) == 0;
}
util::HashValue StreamBuffer::hash() const { util::HashValue StreamBuffer::hash() const {
uint64_t hash1 = 0x82A7912E7893AC87; uint64_t hash1 = 0x82A7912E7893AC87;
uint64_t hash2 = 0x81D402740DE458F3; uint64_t hash2 = 0x81D402740DE458F3;
@@ -448,90 +460,90 @@ LuaDefine(unittests_streambuffer, "c") {
StreamBuffer sb11(11, true); StreamBuffer sb11(11, true);
// Check the initial state. // Check the initial state.
assert(sb11.layout_is(0, 0, 11)); LuaAssert(L, sb11.layout_is(0, 0, 11));
// Write a few bytes. // Write a few bytes.
write_ztbytes(&sb11, "abcdef"); write_ztbytes(&sb11, "abcdef");
assert(sb11.layout_is(0, 6, 5)); LuaAssert(L, sb11.layout_is(0, 6, 5));
// Try reading some bytes. // Try reading some bytes.
assert(streq("abcd", sb11.read_bytes(4))); LuaAssert(L, streq("abcd", sb11.read_bytes(4)));
assert(sb11.layout_is(4, 2, 5)); LuaAssert(L, sb11.layout_is(4, 2, 5));
// Put back two bytes. // Put back two bytes.
sb11.unread_to(2); sb11.unread_to(2);
assert(sb11.layout_is(2, 4, 5)); LuaAssert(L, sb11.layout_is(2, 4, 5));
// Read some more bytes. // Read some more bytes.
assert(streq("cdef", sb11.read_bytes(4))); LuaAssert(L, streq("cdef", sb11.read_bytes(4)));
assert(sb11.layout_is(6, 0, 5)); LuaAssert(L, sb11.layout_is(6, 0, 5));
// Reading bytes now should raise an EOF and should not alter layout // Reading bytes now should raise an EOF and should not alter layout
try { try {
sb11.read_bytes(1); sb11.read_bytes(1);
assert(false && "This should have thrown an exception"); LuaAssert(L, false && "This should have thrown an exception");
} catch (StreamEof) {} } catch (StreamEof) {}
assert(sb11.layout_is(6, 0, 5)); LuaAssert(L, sb11.layout_is(6, 0, 5));
// Write some more bytes into the stream, forcing a shift-left // Write some more bytes into the stream, forcing a shift-left
write_ztbytes(&sb11, "ghijkl"); write_ztbytes(&sb11, "ghijkl");
assert(sb11.layout_is(0, 6, 5)); LuaAssert(L, sb11.layout_is(0, 6, 5));
// Test buffer wrapping a little more. // Test buffer wrapping a little more.
assert(streq("ghi", sb11.read_bytes(3))); LuaAssert(L, streq("ghi", sb11.read_bytes(3)));
assert(sb11.layout_is(3, 3, 5)); LuaAssert(L, sb11.layout_is(3, 3, 5));
write_ztbytes(&sb11, "mnopqr"); write_ztbytes(&sb11, "mnopqr");
assert(sb11.layout_is(0, 9, 2)); LuaAssert(L, sb11.layout_is(0, 9, 2));
assert(streq("jklmnopqr", sb11.read_bytes(9))); LuaAssert(L, streq("jklmnopqr", sb11.read_bytes(9)));
assert(sb11.layout_is(9, 0, 2)); LuaAssert(L, sb11.layout_is(9, 0, 2));
// Test 1-byte integer ops. // Test 1-byte integer ops.
sb11.clear(); sb11.clear();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
sb11.write_int8(i); sb11.write_int8(i);
sb11.write_int8(i+100); sb11.write_int8(i+100);
assert(sb11.read_int8() == i); LuaAssert(L, sb11.read_int8() == i);
assert(sb11.read_int8() == i+100); LuaAssert(L, sb11.read_int8() == i+100);
} }
// Test 2-byte integer ops. // Test 2-byte integer ops.
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
sb11.write_int16(i); sb11.write_int16(i);
sb11.write_int16(i+10000); sb11.write_int16(i+10000);
assert(sb11.read_int16() == i); LuaAssert(L, sb11.read_int16() == i);
assert(sb11.read_int16() == i+10000); LuaAssert(L, sb11.read_int16() == i+10000);
} }
// Test 4-byte integer ops. // Test 4-byte integer ops.
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
sb11.write_int32(i); sb11.write_int32(i);
sb11.write_int32(i+1000000); sb11.write_int32(i+1000000);
assert(sb11.read_int32() == i); LuaAssert(L, sb11.read_int32() == i);
assert(sb11.read_int32() == i+1000000); LuaAssert(L, sb11.read_int32() == i+1000000);
} }
// Test 8-byte integer ops. // Test 8-byte integer ops.
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
sb11.write_int64(i + 1000000); sb11.write_int64(i + 1000000);
assert(sb11.read_int64() == i + 1000000); LuaAssert(L, sb11.read_int64() == i + 1000000);
} }
// Check the write count and read count accumulator ability. // Check the write count and read count accumulator ability.
sb11.clear(); sb11.clear();
assert(sb11.total_writes() == 0); LuaAssert(L, sb11.total_writes() == 0);
assert(sb11.total_reads() == 0); LuaAssert(L, sb11.total_reads() == 0);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
assert(sb11.total_writes() == i * 8); LuaAssert(L, sb11.total_writes() == i * 8);
sb11.write_int32(i); sb11.write_int32(i);
sb11.write_int32(i+1000000); sb11.write_int32(i+1000000);
assert(sb11.total_reads() == i * 8); LuaAssert(L, sb11.total_reads() == i * 8);
assert(sb11.read_int32() == i); LuaAssert(L, sb11.read_int32() == i);
assert(sb11.read_int32() == i+1000000); LuaAssert(L, sb11.read_int32() == i+1000000);
} }
// Try clearing the buffer. // Try clearing the buffer.
sb11.clear(); sb11.clear();
assert(sb11.layout_is(0, 0, 11)); LuaAssert(L, sb11.layout_is(0, 0, 11));
// Write a number then overwrite. // Write a number then overwrite.
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
@@ -541,10 +553,10 @@ LuaDefine(unittests_streambuffer, "c") {
sb11.write_int16(56); sb11.write_int16(56);
sb11.write_int16(78); sb11.write_int16(78);
sb11.overwrite_int16(wc, 90); sb11.overwrite_int16(wc, 90);
assert(sb11.read_int16() == 12); LuaAssert(L, sb11.read_int16() == 12);
assert(sb11.read_int16() == 90); LuaAssert(L, sb11.read_int16() == 90);
assert(sb11.read_int16() == 56); LuaAssert(L, sb11.read_int16() == 56);
assert(sb11.read_int16() == 78); LuaAssert(L, sb11.read_int16() == 78);
} }
// Try compact string encoding. // Try compact string encoding.
@@ -552,10 +564,21 @@ LuaDefine(unittests_streambuffer, "c") {
sb11.write_string("abc"); sb11.write_string("abc");
sb11.write_string(""); sb11.write_string("");
sb11.write_string("de"); sb11.write_string("de");
assert(sb11.layout_is(0, 8, 3)); LuaAssert(L, sb11.layout_is(0, 8, 3));
assert(sb11.read_string() == "abc"); LuaAssert(L, sb11.read_string() == "abc");
assert(sb11.read_string() == ""); LuaAssert(L, sb11.read_string() == "");
assert(sb11.read_string() == "de"); LuaAssert(L, sb11.read_string() == "de");
// Make sure that contents_equal is not obviously broken.
StreamBuffer eqsb1, eqsb2;
eqsb1.write_int32(12);
eqsb1.write_int32(34);
eqsb2.write_int32(34);
LuaAssert(L, !eqsb1.contents_equal(&eqsb2));
eqsb1.read_int32();
LuaAssert(L, eqsb1.contents_equal(&eqsb2));
eqsb1.write_int32(34);
LuaAssert(L, !eqsb1.contents_equal(&eqsb2));
return 0; return 0;
} }

View File

@@ -256,6 +256,9 @@ public:
// Get the total number of bytes ever written to this buffer. // Get the total number of bytes ever written to this buffer.
int64_t total_writes() const; int64_t total_writes() const;
// Amount of data inside the buffer.
int64_t fill() const;
// Discard all data. Reset total read and write counts. // Discard all data. Reset total read and write counts.
// Frees up as much space as possible. // Frees up as much space as possible.
void clear(); void clear();
@@ -353,6 +356,9 @@ public:
// Copy the entire contents of this streambuffer into another one. // Copy the entire contents of this streambuffer into another one.
void copy_into(StreamBuffer *sb); void copy_into(StreamBuffer *sb);
// Compare the contents of this streambuffer to another one.
bool contents_equal(const StreamBuffer *sb) const;
// Calculate a noncryptographic but good hash of what's in the buffer. // Calculate a noncryptographic but good hash of what's in the buffer.
util::HashValue hash() const; util::HashValue hash() const;

View File

@@ -180,6 +180,8 @@ void TextGame::check_redirects() {
void TextGame::run() void TextGame::run()
{ {
world_.reset(new World(util::WORLD_TYPE_STANDALONE)); world_.reset(new World(util::WORLD_TYPE_STANDALONE));
world_->update_source();
world_->run_unittests();
actor_id_ = world_->create_login_actor(); actor_id_ = world_->create_login_actor();
std::cerr << "Login actor ID: " << actor_id_ << std::endl; std::cerr << "Login actor ID: " << actor_id_ << std::endl;
console_.clear(); console_.clear();

View File

@@ -16,6 +16,19 @@
namespace util { namespace util {
std::string id_vector_debug_string(const IdVector &idv) {
std::ostringstream oss;
bool first = true;
for (int64_t id : idv) {
if (!first) oss << ",";
oss << id;
first = false;
}
return oss.str();
}
IdVector sort_union_id_vectors(const IdVector &v1, const IdVector &v2) { IdVector sort_union_id_vectors(const IdVector &v1, const IdVector &v2) {
IdVector result(v1.size() + v2.size()); IdVector result(v1.size() + v2.size());
int next = 0; int next = 0;

View File

@@ -23,6 +23,9 @@ using StringVec = std::vector<std::string>;
using HashValue = std::pair<uint64_t, uint64_t>; using HashValue = std::pair<uint64_t, uint64_t>;
using IdVector = std::vector<int64_t>; using IdVector = std::vector<int64_t>;
// ID vector debug string.
std::string id_vector_debug_string(const IdVector &idv);
// Unions and sorts two ID vectors. // Unions and sorts two ID vectors.
IdVector sort_union_id_vectors(const IdVector &v1, const IdVector &v2); IdVector sort_union_id_vectors(const IdVector &v1, const IdVector &v2);

View File

@@ -31,7 +31,7 @@ World::World(util::WorldType wt) {
world_type_ = wt; world_type_ = wt;
// Initialize the ID allocator in master mode. // Initialize the ID allocator in master mode.
if (wt == util::WORLD_TYPE_MASTER) { if (wt == util::WORLD_TYPE_MASTER || wt == util::WORLD_TYPE_STANDALONE) {
id_global_pool_.init_master(); id_global_pool_.init_master();
} else { } else {
id_global_pool_.init_synch(); id_global_pool_.init_synch();
@@ -50,19 +50,11 @@ World::World(util::WorldType wt) {
// Create the tangibles table in the registry. // Create the tangibles table in the registry.
LS.rawset(LuaRegistry, "tangibles", LuaNewTable); LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
// Initialize the SourceDB // Initialize the SourceDB. At this stage, the sourcedb is
// empty, so it's just populating the lua builtins.
source_db_.init(state()); source_db_.init(state());
source_db_.rebuild(); source_db_.rebuild();
// Do standalone initializations.
if (world_type_ == util::WORLD_TYPE_STANDALONE) {
// Load the lua source from disk then rebuild the environment.
source_db_.update();
source_db_.rebuild();
// Run unit tests.
source_db_.run_unittests();
}
LS.result(); LS.result();
assert (stack_is_clear()); assert (stack_is_clear());
} }
@@ -161,6 +153,29 @@ void World::tangible_delete(lua_State *L, int64_t id) {
LS.result(); LS.result();
} }
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);
}
std::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);
}
util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_nowhere) const { util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_nowhere) const {
const Tangible *player = tangible_get(player_id); const Tangible *player = tangible_get(player_id);
if (player == nullptr) { if (player == nullptr) {
@@ -177,6 +192,12 @@ util::IdVector World::get_near(int64_t player_id, float radius, bool exclude_now
} }
Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) { Tangible *World::tangible_make(lua_State *L, int64_t id, bool pushdb) {
// Get a state if we don't already have one.
if (L == nullptr) {
L = state();
assert(!pushdb);
}
LuaVar tangibles, metatab; LuaVar tangibles, metatab;
LuaRet database; LuaRet database;
LuaStack LS(L, tangibles, database, metatab); LuaStack LS(L, tangibles, database, metatab);
@@ -446,7 +467,7 @@ void World::run_scheduled_threads(int64_t clk) {
void World::serialize(StreamBuffer *sb) { void World::serialize(StreamBuffer *sb) {
assert(stack_is_clear()); assert(stack_is_clear());
assert(redirects_.empty()); assert(redirects_.empty());
int64_t wc0 = sb->total_writes(); // int64_t wc0 = sb->total_writes();
lua_snap_.serialize(sb); lua_snap_.serialize(sb);
id_global_pool_.serialize(sb); id_global_pool_.serialize(sb);
thread_sched_.serialize(sb); thread_sched_.serialize(sb);
@@ -455,8 +476,8 @@ void World::serialize(StreamBuffer *sb) {
sb->write_int64(p.first); sb->write_int64(p.first);
p.second->serialize(sb); p.second->serialize(sb);
} }
int64_t wc1 = sb->total_writes(); // int64_t wc1 = sb->total_writes();
std::cerr << "World serialized to " << wc1-wc0 << " bytes." << std::endl; // std::cerr << "World serialized to " << wc1-wc0 << " bytes." << std::endl;
assert(stack_is_clear()); assert(stack_is_clear());
} }
@@ -506,13 +527,12 @@ void World::rollback() {
void World::difference_transmit(int64_t actor_id, const World *master, StreamBuffer *sb) { void World::difference_transmit(int64_t actor_id, const World *master, StreamBuffer *sb) {
StreamBuffer tsb; StreamBuffer tsb;
// Get the actor in both models. // Get the actor in both models. s_actor may be nil.
const Tangible *s_actor = tangible_get(actor_id); const Tangible *s_actor = tangible_get(actor_id);
const Tangible *m_actor = master->tangible_get(actor_id); const Tangible *m_actor = master->tangible_get(actor_id);
assert(s_actor != nullptr);
assert(m_actor != nullptr); assert(m_actor != nullptr);
// Pass one: update the actor essentials. // Pass one: update the actor essentials. Create actor if doesn't exist.
assert(tsb.at_eof()); assert(tsb.at_eof());
diff_actor_essentials(m_actor, s_actor, &tsb); diff_actor_essentials(m_actor, s_actor, &tsb);
tsb.copy_into(sb); tsb.copy_into(sb);
@@ -523,7 +543,7 @@ void World::difference_transmit(int64_t actor_id, const World *master, StreamBuf
util::IdVector visible = util::sort_union_id_vectors( util::IdVector visible = util::sort_union_id_vectors(
master->get_near(actor_id, 100.0, true), master->get_near(actor_id, 100.0, true),
this->get_near(actor_id, 100.0, true)); this->get_near(actor_id, 100.0, true));
TanVector m_visible = tangible_get_all(visible); TanVector m_visible = master->tangible_get_all(visible);
TanVector s_visible = tangible_get_all(visible); TanVector s_visible = tangible_get_all(visible);
assert(m_visible.size() == s_visible.size()); assert(m_visible.size() == s_visible.size());
@@ -549,17 +569,33 @@ void World::difference_transmit(int64_t actor_id, const World *master, StreamBuf
assert(tsb.at_eof()); assert(tsb.at_eof());
} }
void World::apply_differences(StreamBuffer *sb) {
patch_actor_essentials(sb);
patch_visible_animations(sb);
}
void World::diff_actor_essentials(const Tangible *m_actor, const Tangible *s_actor, StreamBuffer *sb) { void World::diff_actor_essentials(const Tangible *m_actor, const Tangible *s_actor, StreamBuffer *sb) {
sb->write_int64(m_actor->id()); sb->write_int64(m_actor->id());
if (s_actor == nullptr) {
m_actor->id_player_pool_.serialize(sb);
m_actor->anim_queue_.serialize(sb);
} else {
s_actor->id_player_pool_.diff(m_actor->id_player_pool_, sb); s_actor->id_player_pool_.diff(m_actor->id_player_pool_, sb);
s_actor->anim_queue_.diff(m_actor->anim_queue_, sb); s_actor->anim_queue_.diff(m_actor->anim_queue_, sb);
}
} }
void World::patch_actor_essentials(StreamBuffer *sb) { void World::patch_actor_essentials(StreamBuffer *sb) {
int64_t actor_id = sb->read_int64(); int64_t actor_id = sb->read_int64();
Tangible *s_actor = tangible_get(actor_id); Tangible *s_actor = tangible_get(actor_id);
if (s_actor == nullptr) {
s_actor = tangible_make(nullptr, actor_id, false);
s_actor->id_player_pool_.deserialize(sb);
s_actor->anim_queue_.deserialize(sb);
} else {
s_actor->id_player_pool_.patch(sb); s_actor->id_player_pool_.patch(sb);
s_actor->anim_queue_.patch(sb); s_actor->anim_queue_.patch(sb);
}
s_actor->update_plane_item(); s_actor->update_plane_item();
} }
@@ -575,7 +611,7 @@ void World::diff_visible_animations(const TanVector &mvis, const TanVector &svis
if (st == nullptr) { if (st == nullptr) {
count += 1; count += 1;
sb->write_int64(mt->id()); sb->write_int64(mt->id());
mt->anim_queue_.serialize_size_and_steps(sb); mt->anim_queue_.serialize(sb);
} }
} }
sb->overwrite_int32(count_pos, count); sb->overwrite_int32(count_pos, count);
@@ -620,7 +656,7 @@ void World::patch_visible_animations(StreamBuffer *sb) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int64_t id = sb->read_int64(); int64_t id = sb->read_int64();
Tangible *t = tangible_make(state(), id, false); Tangible *t = tangible_make(state(), id, false);
t->anim_queue_.deserialize_size_and_steps(sb); t->anim_queue_.deserialize(sb);
t->update_plane_item(); t->update_plane_item();
} }
@@ -791,3 +827,30 @@ LuaDefine(world_getregistry, "f") {
lua_pushvalue(L, LUA_REGISTRYINDEX); lua_pushvalue(L, LUA_REGISTRYINDEX);
return 1; return 1;
} }
static bool worlds_identical(const std::unique_ptr<World> &w1, const std::unique_ptr<World> &w2) {
StreamBuffer sbw1, sbw2;
w1->serialize(&sbw1);
w2->serialize(&sbw2);
return sbw1.contents_equal(&sbw2);
}
LuaDefine(unittests_world, "c") {
std::unique_ptr<World> m, ss, cs;
StreamBuffer sb;
m.reset(new World(util::WORLD_TYPE_MASTER));
ss.reset(new World(util::WORLD_TYPE_S_SYNC));
cs.reset(new World(util::WORLD_TYPE_C_SYNC));
m->tangible_make(0, 123, false);
m->tangible_make(0, 345, false);
LuaAssertStrEq(L, m->tangible_ids_debug_string(), "123,345");
ss->difference_transmit(123, m.get(), &sb);
cs->apply_differences(&sb);
LuaAssertStrEq(L, ss->tangible_ids_debug_string(), "123,345");
LuaAssert(L, worlds_identical(ss, cs));
return 0;
}

View File

@@ -84,9 +84,7 @@ public:
// Constructor. // Constructor.
// //
// The constructor also calls 'lua_open' to create a new // The constructor also calls 'lua_open' to create a new
// lua interpreter for this world model. The lua interpreter // lua interpreter for this world model.
// is stored in world::lua_state_. A significant amount of
// initial setup is done by this constructor.
// //
World(util::WorldType wt); World(util::WorldType wt);
@@ -160,6 +158,14 @@ public:
// //
void invoke(const Invocation &inv); void invoke(const Invocation &inv);
// Update the source database from disk.
//
void update_source() { source_db_.update(); source_db_.rebuild(); }
// Run all unit tests.
//
void run_unittests() { source_db_.run_unittests(); }
// fetch_global_pointer // fetch_global_pointer
// //
// Given a lua state, fetch the world model associated with // Given a lua state, fetch the world model associated with
@@ -190,7 +196,25 @@ public:
// Note that difference_transmit applies the differences to the server // Note that difference_transmit applies the differences to the server
// synchronous model, so this is only used by the client synchronous model. // synchronous model, so this is only used by the client synchronous model.
// //
void apply_differences(int64_t actor, StreamBuffer *sb); void apply_differences(StreamBuffer *sb);
public:
////////////////////////////////////////////////////////////////////////////
//
// TESTING SUPPORT
//
// The following functions are not designed to be useful for production
// code, they're designed to be helpful for unit testing.
//
////////////////////////////////////////////////////////////////////////////
// Add a 'walkto' animation to the specified tangible.
//
void tangible_walkto(int64_t id, int64_t animid, float x, float y);
// Get a list of all existing tangibles as a comma-separated string.
//
std::string tangible_ids_debug_string() const;
private: private:
// Run any threads which according to the scheduler queue are ready. // Run any threads which according to the scheduler queue are ready.