653 lines
18 KiB
C++
653 lines
18 KiB
C++
#include <limits>
|
|
#include <map>
|
|
#include "luastack.hpp"
|
|
#include "animqueue.hpp"
|
|
#include "streambuffer.hpp"
|
|
#include <ostream>
|
|
#include <sstream>
|
|
|
|
AnimStep::AnimStep() {
|
|
clear();
|
|
}
|
|
|
|
AnimStep::~AnimStep() {}
|
|
|
|
|
|
void AnimStep::clear() {
|
|
id_ = 0;
|
|
bits_ = 0;
|
|
action_ = "";
|
|
facing_ = 0;
|
|
xyz_ = util::XYZ(0,0,0);
|
|
graphic_ = "";
|
|
plane_ = "";
|
|
}
|
|
|
|
void AnimStep::set_action(const std::string &act) {
|
|
action_ = act;
|
|
}
|
|
|
|
void AnimStep::set_facing(float f) {
|
|
bits_ |= HAS_FACING;
|
|
facing_ = f;
|
|
}
|
|
|
|
void AnimStep::set_x(float f) {
|
|
bits_ |= HAS_X;
|
|
xyz_.x = f;
|
|
}
|
|
|
|
void AnimStep::set_y(float f) {
|
|
bits_ |= HAS_Y;
|
|
xyz_.y = f;
|
|
}
|
|
|
|
void AnimStep::set_z(float f) {
|
|
bits_ |= HAS_Z;
|
|
xyz_.z = f;
|
|
}
|
|
|
|
void AnimStep::set_xyz(const util::XYZ &xyz) {
|
|
bits_ |= (HAS_X | HAS_Y | HAS_Z);
|
|
xyz_ = xyz;
|
|
}
|
|
|
|
void AnimStep::set_graphic(const std::string &g) {
|
|
bits_ |= HAS_GRAPHIC;
|
|
graphic_ = g;
|
|
}
|
|
|
|
void AnimStep::set_plane(const std::string &p) {
|
|
bits_ |= HAS_PLANE;
|
|
plane_ = p;
|
|
}
|
|
|
|
|
|
bool AnimStep::exactly_equal(const AnimStep &other) const {
|
|
if (id_ != other.id_) return false;
|
|
if (bits_ != other.bits_) return false;
|
|
if (action_ != other.action_) return false;
|
|
if (facing_ != other.facing_) return false;
|
|
if (xyz_ != other.xyz_) return false;
|
|
if (graphic_ != other.graphic_) return false;
|
|
if (plane_ != other.plane_) return false;
|
|
return true;
|
|
}
|
|
|
|
bool AnimStep::logically_equal(const AnimStep &other) const {
|
|
if (id_ != other.id_) return false;
|
|
if (bits_ != other.bits_) return false;
|
|
if (action_ != other.action_) return false;
|
|
if (has_facing() && (facing_ != other.facing_)) return false;
|
|
if (has_x() && (xyz_.x != other.xyz_.x)) return false;
|
|
if (has_y() && (xyz_.y != other.xyz_.y)) return false;
|
|
if (has_z() && (xyz_.z != other.xyz_.z)) return false;
|
|
if (has_graphic() && (graphic_ != other.graphic_)) return false;
|
|
if (has_plane() && (plane_ != other.plane_)) return false;
|
|
return true;
|
|
}
|
|
|
|
bool AnimStep::state_equal(const AnimStep &other) const {
|
|
if (facing_ != other.facing_) return false;
|
|
if (xyz_ != other.xyz_) return false;
|
|
if (graphic_ != other.graphic_) return false;
|
|
if (plane_ != other.plane_) return false;
|
|
return true;
|
|
}
|
|
|
|
void AnimStep::write_into(StreamBuffer *sb) const {
|
|
sb->write_int64(id_);
|
|
sb->write_int16(bits_);
|
|
sb->write_string(action_);
|
|
if (has_facing()) {
|
|
sb->write_float(facing_);
|
|
}
|
|
if (has_x()) {
|
|
sb->write_float(xyz_.x);
|
|
}
|
|
if (has_y()) {
|
|
sb->write_float(xyz_.y);
|
|
}
|
|
if (has_z()) {
|
|
sb->write_float(xyz_.z);
|
|
}
|
|
if (has_graphic()) {
|
|
sb->write_string(graphic_);
|
|
}
|
|
if (has_plane()) {
|
|
sb->write_string(plane_);
|
|
}
|
|
}
|
|
|
|
void AnimStep::read_from(StreamBuffer *sb) {
|
|
id_ = sb->read_int64();
|
|
bits_ = sb->read_int16();
|
|
action_ = sb->read_string();
|
|
if (has_facing()) {
|
|
facing_ = sb->read_float();
|
|
}
|
|
if (has_x()) {
|
|
xyz_.x = sb->read_float();
|
|
}
|
|
if (has_y()) {
|
|
xyz_.y = sb->read_float();
|
|
}
|
|
if (has_z()) {
|
|
xyz_.z = sb->read_float();
|
|
}
|
|
if (has_graphic()) {
|
|
graphic_ = sb->read_string();
|
|
}
|
|
if (has_plane()) {
|
|
plane_ = sb->read_string();
|
|
}
|
|
}
|
|
|
|
|
|
void AnimStep::from_lua_store_string(lua_State *L, int idx, std::string *target, int16_t bits, const char *name) {
|
|
if ((bits_ & bits)||(*target != "")) {
|
|
luaL_error(L, "specified %s twice", name);
|
|
}
|
|
if (lua_type(L, idx) != LUA_TSTRING) {
|
|
luaL_error(L, "Expected %s to be a string", name);
|
|
}
|
|
*target = lua_tostring(L, idx);
|
|
bits_ |= bits;
|
|
}
|
|
|
|
void AnimStep::from_lua_store_number(lua_State *L, int idx, float *target, float offset, int16_t bits, const char *name) {
|
|
if (bits_ & bits) {
|
|
luaL_error(L, "specified %s twice", name);
|
|
}
|
|
if (lua_type(L, idx) != LUA_TNUMBER) {
|
|
luaL_error(L, "Expected %s to be a number", name);
|
|
}
|
|
*target = lua_tonumber(L, idx) + offset;
|
|
bits_ |= bits;
|
|
}
|
|
|
|
void AnimStep::from_lua(lua_State *L, int idx, const AnimStep &qback) {
|
|
LuaSpecial tab(idx);
|
|
LuaVar key, value;
|
|
LuaStack LS(L, key, value);
|
|
if (!LS.istable(tab)) {
|
|
luaL_error(L, "animation spec must be a table");
|
|
}
|
|
LS.set(key, LuaNil);
|
|
while (LS.next(tab, key, value)) {
|
|
if (!LS.isstring(key)) {
|
|
luaL_error(L, "animation specs must be key/value where key is a string");
|
|
}
|
|
std::string skey = LS.ckstring(key);
|
|
if (skey == "action") {
|
|
from_lua_store_string(L, value.index(), &action_, 0, "action");
|
|
} else if (skey == "graphic") {
|
|
from_lua_store_string(L, value.index(), &graphic_, HAS_GRAPHIC, "graphic");
|
|
} else if (skey == "plane") {
|
|
from_lua_store_string(L, value.index(), &plane_, HAS_PLANE, "plane");
|
|
} else if (skey == "x") {
|
|
from_lua_store_number(L, value.index(), &xyz_.x, 0.0, HAS_X, "X coordinate");
|
|
} else if (skey == "y") {
|
|
from_lua_store_number(L, value.index(), &xyz_.y, 0.0, HAS_Y, "Z coordinate");
|
|
} else if (skey == "z") {
|
|
from_lua_store_number(L, value.index(), &xyz_.z, 0.0, HAS_Z, "Z coordinate");
|
|
} else if (skey == "dx") {
|
|
from_lua_store_number(L, value.index(), &xyz_.x, qback.xyz().x, HAS_X, "X coordinate");
|
|
} else if (skey == "dy") {
|
|
from_lua_store_number(L, value.index(), &xyz_.y, qback.xyz().y, HAS_Y, "Y coordinate");
|
|
} else if (skey == "dz") {
|
|
from_lua_store_number(L, value.index(), &xyz_.z, qback.xyz().z, HAS_Z, "Z coordinate");
|
|
} else if (skey == "facing") {
|
|
from_lua_store_number(L, value.index(), &facing_, 0.0, HAS_FACING, "facing");
|
|
} else {
|
|
luaL_error(L, "Unrecognized animation spec: %s", skey.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimStep::keep_state_only() {
|
|
bits_ = HAS_EVERYTHING;
|
|
id_ = 0;
|
|
action_ = "";
|
|
}
|
|
|
|
void AnimStep::echo(const AnimStep &prev) {
|
|
if (!has_facing()) {
|
|
facing_ = prev.facing_;
|
|
}
|
|
if (!has_x()) {
|
|
xyz_.x = prev.xyz_.x;
|
|
}
|
|
if (!has_y()) {
|
|
xyz_.y = prev.xyz_.y;
|
|
}
|
|
if (!has_z()) {
|
|
xyz_.z = prev.xyz_.z;
|
|
}
|
|
if (!has_graphic()) {
|
|
graphic_ = prev.graphic_;
|
|
}
|
|
if (!has_plane()) {
|
|
plane_ = prev.plane_;
|
|
}
|
|
}
|
|
|
|
bool AnimStep::echoes(const AnimStep &prev) const {
|
|
if (!has_facing() && (facing_ != prev.facing_)) {
|
|
return false;
|
|
}
|
|
if (!has_x() && (xyz_.x != prev.xyz_.x)) {
|
|
return false;
|
|
}
|
|
if (!has_y() && (xyz_.y != prev.xyz_.y)) {
|
|
return false;
|
|
}
|
|
if (!has_z() && (xyz_.z != prev.xyz_.z)) {
|
|
return false;
|
|
}
|
|
if (!has_graphic() && (graphic_ != prev.graphic_)) {
|
|
return false;
|
|
}
|
|
if (!has_plane() && (plane_ != prev.plane_)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string AnimStep::debug_string() const {
|
|
std::ostringstream oss;
|
|
oss << "id=" << id();
|
|
oss << " action=" << action();
|
|
if (has_plane()) {
|
|
oss << " plane=" << plane();
|
|
}
|
|
if (has_x()) {
|
|
oss << " x=" << xyz().x;
|
|
}
|
|
if (has_y()) {
|
|
oss << " y=" << xyz().y;
|
|
}
|
|
if (has_z()) {
|
|
oss << " z=" << xyz().z;
|
|
}
|
|
if (has_facing()) {
|
|
oss << " facing=" << facing();
|
|
}
|
|
if (has_graphic()) {
|
|
oss << " graphic=" << graphic();
|
|
}
|
|
return oss.str();
|
|
}
|
|
|
|
|
|
bool AnimStep::from_string(const std::string &config) {
|
|
clear();
|
|
util::StringVec parts = util::split(config, ' ');
|
|
for (int i = 0; i < int(parts.size()); i++) {
|
|
const std::string &part = parts[i];
|
|
if (part == "") continue;
|
|
util::StringVec lr = util::split(part, '=');
|
|
if (lr.size() != 2) return false;
|
|
const std::string &key = lr[0];
|
|
const std::string &val = lr[1];
|
|
if (key == "action") {
|
|
action_ = val;
|
|
} else if (key == "id") {
|
|
int64_t id = util::strtoint(val, -1);
|
|
if (id < 0) return false;
|
|
id_ = id;
|
|
} else if (key == "plane") {
|
|
set_plane(val);
|
|
} else if (key == "x") {
|
|
double v = util::strtodouble(val);
|
|
if (std::isnan(v)) return false;
|
|
set_x(v);
|
|
} else if (key == "y") {
|
|
double v = util::strtodouble(val);
|
|
if (std::isnan(v)) return false;
|
|
set_y(v);
|
|
} else if (key == "z") {
|
|
double v = util::strtodouble(val);
|
|
if (std::isnan(v)) return false;
|
|
set_z(v);
|
|
} else if (key == "facing") {
|
|
double v = util::strtodouble(val);
|
|
if (std::isnan(v)) return false;
|
|
set_facing(v);
|
|
} else if (key == "graphic") {
|
|
set_graphic(val);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
AnimQueue::AnimQueue(util::WorldType wt) {
|
|
version_autoinc_ = (wt == util::WORLD_TYPE_MASTER);
|
|
size_limit_ = 10; // Default size limit.
|
|
steps_.emplace_back();
|
|
steps_.front().keep_state_only();
|
|
version_number_ = version_autoinc_ ? 1 : 0;
|
|
}
|
|
|
|
void AnimQueue::mutated() {
|
|
if (version_autoinc_) {
|
|
version_number_ += 1;
|
|
} else {
|
|
version_number_ = 0;
|
|
}
|
|
}
|
|
|
|
bool AnimQueue::size_and_steps_equal(const AnimQueue &other) const {
|
|
if (size_limit_ != other.size_limit_) {
|
|
return false;
|
|
}
|
|
if (steps_.size() != other.steps_.size()) {
|
|
return false;
|
|
}
|
|
for (int i = 0; i < int(steps_.size()); i++) {
|
|
if (!steps_[i].exactly_equal(other.steps_[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void AnimQueue::full_clear_and_set_limit(int n) {
|
|
assert(n >= 1);
|
|
steps_.clear();
|
|
steps_.emplace_back();
|
|
steps_.front().keep_state_only();
|
|
size_limit_ = n;
|
|
version_number_ = version_autoinc_ ? 1 : 0;
|
|
}
|
|
void AnimQueue::set_limit(int n) {
|
|
assert(n >= 1);
|
|
size_limit_ = n;
|
|
if (int(steps_.size()) > n) {
|
|
while (int(steps_.size()) > n) {
|
|
steps_.pop_front();
|
|
}
|
|
steps_.front().keep_state_only();
|
|
}
|
|
mutated();
|
|
}
|
|
|
|
void AnimQueue::add(int64_t id, const AnimStep &step) {
|
|
AnimStep copy = step;
|
|
copy.echo(steps_.back());
|
|
copy.id_ = id;
|
|
steps_.push_back(copy);
|
|
while (int(steps_.size()) > size_limit_) {
|
|
steps_.pop_front();
|
|
}
|
|
steps_.front().keep_state_only();
|
|
mutated();
|
|
}
|
|
|
|
bool AnimQueue::valid() const {
|
|
// Size limit must be between 2 and 250
|
|
if ((size_limit_ < 1) || (size_limit_ > 250)) {
|
|
return false;
|
|
}
|
|
// Animqueue must have at least one step, and no more than 250.
|
|
if (steps_.empty()) {
|
|
return false;
|
|
}
|
|
if (steps_.size() > 250) {
|
|
return false;
|
|
}
|
|
// First action should be blank.
|
|
if (steps_[0].action_ != "") {
|
|
return false;
|
|
}
|
|
// First step should have all bits.
|
|
if (steps_[0].bits_ != AnimStep::HAS_EVERYTHING) {
|
|
return false;
|
|
}
|
|
// Any unset bit should correspond to a value copied from the previous step.
|
|
for (size_t i = 1; i < steps_.size(); i++) {
|
|
const AnimStep &prev = steps_[i - 1];
|
|
const AnimStep &curr = steps_[i];
|
|
if (!curr.echoes(prev)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string AnimQueue::debug_string() const {
|
|
std::ostringstream oss;
|
|
oss << "version=" << version_number();
|
|
oss << "; limit=" << size_limit();
|
|
for (int i = 0; i < int(size()); i++) {
|
|
oss << "; " << nth(i).debug_string();
|
|
}
|
|
return oss.str();
|
|
}
|
|
|
|
void AnimQueue::serialize(StreamBuffer *sb) const {
|
|
assert(valid()); // can't serialize an invalid animqueue.
|
|
sb->write_uint8(size_limit_);
|
|
sb->write_uint8(steps_.size());
|
|
for (const AnimStep &step : steps_) {
|
|
step.write_into(sb);
|
|
}
|
|
}
|
|
|
|
void AnimQueue::deserialize(StreamBuffer *sb) {
|
|
size_limit_ = sb->read_uint8();
|
|
size_t nsteps = sb->read_uint8();
|
|
steps_.resize(nsteps);
|
|
for (size_t i = 0; i < nsteps; i++) {
|
|
AnimStep &step = steps_[i];
|
|
step.read_from(sb);
|
|
if (i > 0) step.echo(steps_[i - 1]);
|
|
}
|
|
version_number_ = 0;
|
|
}
|
|
|
|
bool AnimQueue::need_patch(const AnimQueue &auth) const {
|
|
// Sanity check.
|
|
assert(valid());
|
|
assert(auth.valid());
|
|
|
|
// Fast path to detect equivalence.
|
|
if (version_number_ == auth.version_number_) {
|
|
return false;
|
|
}
|
|
|
|
// Otherwise, do a direct comparison.
|
|
return !size_and_steps_equal(auth);
|
|
}
|
|
|
|
|
|
void AnimQueue::diff(const AnimQueue &auth, StreamBuffer *sb) const {
|
|
// Write the first element.
|
|
sb->write_uint8(auth.steps_.size());
|
|
sb->write_uint8(auth.size_limit_);
|
|
const AnimStep &first = auth.steps_[0];
|
|
int match = 0;
|
|
while ((match < int(steps_.size())) && (!steps_[match].state_equal(first))) {
|
|
match++;
|
|
}
|
|
if (match == int(steps_.size())) {
|
|
sb->write_uint8(255);
|
|
first.write_into(sb);
|
|
} else {
|
|
sb->write_uint8(match);
|
|
}
|
|
|
|
// Index the remaining elements by id.
|
|
std::map<uint64_t, int> index;
|
|
for (int i = 1; i < int(steps_.size()); i++) {
|
|
index[steps_[i].id_] = i;
|
|
}
|
|
|
|
// Write the remaining elements.
|
|
for (int i = 1; i < int(auth.steps_.size()); i++) {
|
|
const AnimStep &step = auth.steps_[i];
|
|
auto iter = index.find(step.id());
|
|
if (iter == index.end()) {
|
|
sb->write_uint8(255);
|
|
step.write_into(sb);
|
|
} else {
|
|
const AnimStep &local = steps_[iter->second];
|
|
if (local.exactly_equal(step)) {
|
|
sb->write_uint8(iter->second);
|
|
} else {
|
|
sb->write_uint8(255);
|
|
step.write_into(sb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AnimQueue::patch(StreamBuffer *sb) {
|
|
int len = sb->read_uint8();
|
|
size_limit_ = sb->read_uint8();
|
|
// Decode the diff, stop at eof.
|
|
std::deque<AnimStep> old = std::move(steps_);
|
|
steps_.clear();
|
|
for (int i = 0; i < len; i++) {
|
|
uint8_t index = sb->read_uint8();
|
|
if (index < 255) {
|
|
assert(index < old.size());
|
|
steps_.push_back(old[index]);
|
|
} else {
|
|
AnimStep step;
|
|
step.read_from(sb);
|
|
steps_.push_back(step);
|
|
}
|
|
int size = steps_.size();
|
|
if (size > 1) {
|
|
steps_[size-1].echo(steps_[size-2]);
|
|
} else {
|
|
steps_[0].keep_state_only();
|
|
}
|
|
}
|
|
mutated();
|
|
}
|
|
|
|
void AnimQueue::update_version(const AnimQueue &auth) {
|
|
assert(size_and_steps_equal(auth));
|
|
version_number_ = auth.version_number_;
|
|
}
|
|
|
|
const AnimStep &AnimQueue::back() const {
|
|
return steps_.back();
|
|
}
|
|
|
|
static bool diff_works(const AnimQueue &master, AnimQueue &sync) {
|
|
StreamBuffer sb;
|
|
sync.diff(master, &sb);
|
|
sync.patch(&sb);
|
|
return sync.size_and_steps_equal(master);
|
|
}
|
|
|
|
LuaDefine(unittests_animqueue, "c") {
|
|
// Useful objects.
|
|
AnimStep stp;
|
|
AnimQueue aq(util::WORLD_TYPE_MASTER);
|
|
AnimQueue aqds(util::WORLD_TYPE_S_SYNC);
|
|
StreamBuffer sb;
|
|
|
|
// Debug string of a newly initialized queue
|
|
LuaAssert(L, aq.valid());
|
|
LuaAssertStrEq(L, aq.debug_string(), "version=1; limit=10; id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=");
|
|
|
|
// Test the step setters.
|
|
stp.clear();
|
|
LuaAssertStrEq(L, stp.debug_string(), "id=0 action=");
|
|
stp.set_facing(180);
|
|
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= facing=180");
|
|
stp.set_x(3);
|
|
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= x=3 facing=180");
|
|
stp.set_y(4);
|
|
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= x=3 y=4 facing=180");
|
|
stp.set_z(5);
|
|
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= x=3 y=4 z=5 facing=180");
|
|
stp.set_plane("somewhere");
|
|
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= plane=somewhere x=3 y=4 z=5 facing=180");
|
|
stp.set_graphic("something");
|
|
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= plane=somewhere x=3 y=4 z=5 facing=180 graphic=something");
|
|
|
|
// Test the step debug string parser.
|
|
LuaAssert(L, stp.from_string("id=123 action=walk x=1 y=2 z=3 facing=4 plane=p graphic=g"));
|
|
LuaAssertStrEq(L, stp.debug_string(), "id=123 action=walk plane=p x=1 y=2 z=3 facing=4 graphic=g");
|
|
|
|
// Test that we can clear a queue.
|
|
aq.full_clear_and_set_limit(3);
|
|
LuaAssert(L, aq.valid());
|
|
LuaAssertStrEq(L, aq.debug_string(), "version=1; limit=3; id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=");
|
|
|
|
// Add a step to a queue.
|
|
aq.full_clear_and_set_limit(3);
|
|
LuaAssert(L, stp.from_string("action=walk"));
|
|
aq.add(12345, stp);
|
|
LuaAssertStrEq(L, aq.debug_string(),
|
|
"version=2; limit=3; "
|
|
"id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=; "
|
|
"id=12345 action=walk");
|
|
|
|
// Exceed the length limit, dropping first element.
|
|
aq.full_clear_and_set_limit(3);
|
|
LuaAssert(L, stp.from_string("action=walk plane=foo"));
|
|
aq.add(12345, stp);
|
|
aq.add(12346, stp);
|
|
aq.add(12347, stp);
|
|
LuaAssertStrEq(L, aq.debug_string(),
|
|
"version=4; limit=3; "
|
|
"id=0 action= plane=foo x=0 y=0 z=0 facing=0 graphic=; "
|
|
"id=12346 action=walk plane=foo; "
|
|
"id=12347 action=walk plane=foo"
|
|
);
|
|
|
|
// Test serialization and deserialization.
|
|
aq.full_clear_and_set_limit(5);
|
|
LuaAssert(L, stp.from_string("action=walk x=3 y=4 z=5"));
|
|
aq.add(12345, stp);
|
|
LuaAssert(L, stp.from_string("action=setgraphic graphic=banana"));
|
|
aq.add(12346, stp);
|
|
LuaAssert(L, stp.from_string("action=setfacing facing=301"));
|
|
aq.add(12347, stp);
|
|
|
|
aq.serialize(&sb);
|
|
aqds.deserialize(&sb);
|
|
|
|
LuaAssertStrEq(L, aqds.debug_string(),
|
|
"version=0; limit=5; "
|
|
"id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=; "
|
|
"id=12345 action=walk x=3 y=4 z=5; "
|
|
"id=12346 action=setgraphic graphic=banana; "
|
|
"id=12347 action=setfacing facing=301"
|
|
);
|
|
|
|
// Test difference transmission
|
|
aq.full_clear_and_set_limit(10);
|
|
aqds.full_clear_and_set_limit(10);
|
|
|
|
// Add a single action to the queue and DT
|
|
LuaAssert(L, stp.from_string("action=walk x=3 y=4 z=5"));
|
|
aq.add(12345, stp);
|
|
LuaAssert(L, diff_works(aq, aqds));
|
|
|
|
// Add another action and DT
|
|
LuaAssert(L, stp.from_string("action=fnord plane=where facing=123"));
|
|
aq.add(232, stp);
|
|
LuaAssert(L, diff_works(aq, aqds));
|
|
|
|
// Change the queue size limit.
|
|
aq.set_limit(13);
|
|
LuaAssert(L, diff_works(aq, aqds));
|
|
|
|
// compare again, should be no differences.
|
|
LuaAssert(L, !aqds.need_patch(aq));
|
|
LuaAssert(L, diff_works(aq, aqds));
|
|
|
|
// Discard all but the last action.
|
|
aq.set_limit(1);
|
|
LuaAssert(L, diff_works(aq, aqds));
|
|
|
|
return 0;
|
|
}
|