Spooky hash, smarter animqueue diffs
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include <map>
|
||||
#include "luastack.hpp"
|
||||
#include "animqueue.hpp"
|
||||
#include "streambuffer.hpp"
|
||||
|
||||
AnimStep::AnimStep() {
|
||||
clear();
|
||||
@@ -10,7 +11,6 @@ AnimStep::AnimStep() {
|
||||
AnimStep::~AnimStep() {}
|
||||
|
||||
|
||||
|
||||
void AnimStep::clear() {
|
||||
id_ = 0;
|
||||
bits_ = 0;
|
||||
@@ -252,12 +252,25 @@ bool AnimStep::echoes(const AnimStep &prev) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
AnimQueue::AnimQueue() {
|
||||
AnimQueue::AnimQueue(util::WorldType wt) {
|
||||
world_type_ = wt;
|
||||
size_limit_ = 10; // Default size limit.
|
||||
clear_steps();
|
||||
version_number_ = (wt == util::WORLD_TYPE_MASTER) ? 1 : 0;
|
||||
}
|
||||
|
||||
bool AnimQueue::exactly_equal(const AnimQueue &other) const {
|
||||
void AnimQueue::mutated() {
|
||||
if (world_type_ == util::WORLD_TYPE_MASTER) {
|
||||
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;
|
||||
}
|
||||
@@ -277,19 +290,18 @@ void AnimQueue::clear_steps() {
|
||||
init.action_ = "";
|
||||
init.graphic_ = "";
|
||||
init.plane_ = "";
|
||||
mutated();
|
||||
}
|
||||
|
||||
void AnimQueue::set_size_limit(int n) {
|
||||
assert(n >= 2);
|
||||
assert(n >= 1);
|
||||
if (size_limit_ == n) return;
|
||||
size_limit_ = n;
|
||||
}
|
||||
|
||||
void AnimQueue::keep_only(int n) {
|
||||
if (n < 1) n = 1;
|
||||
while (int(steps_.size()) > n) {
|
||||
while (int(steps_.size()) > size_limit_) {
|
||||
steps_.pop_front();
|
||||
}
|
||||
steps_.front().keep_state_only();
|
||||
mutated();
|
||||
}
|
||||
|
||||
void AnimQueue::add(int64_t id, const AnimStep &step) {
|
||||
@@ -297,15 +309,23 @@ void AnimQueue::add(int64_t id, const AnimStep &step) {
|
||||
copy.echo(steps_.back());
|
||||
copy.id_ = id;
|
||||
steps_.push_back(copy);
|
||||
keep_only(size_limit_);
|
||||
while (int(steps_.size()) > size_limit_) {
|
||||
steps_.pop_front();
|
||||
}
|
||||
steps_.front().keep_state_only();
|
||||
mutated();
|
||||
}
|
||||
|
||||
bool AnimQueue::valid() const {
|
||||
// Animqueue must have at least one step, and no more than 255.
|
||||
// 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() > 255) {
|
||||
if (steps_.size() > 250) {
|
||||
return false;
|
||||
}
|
||||
// First action should be blank.
|
||||
@@ -327,16 +347,18 @@ bool AnimQueue::valid() const {
|
||||
|
||||
void AnimQueue::serialize(StreamBuffer *sb) {
|
||||
assert(valid()); // can't serialize an invalid animqueue.
|
||||
sb->write_int32(size_limit_);
|
||||
sb->write_size(steps_.size());
|
||||
sb->write_int64(version_number_);
|
||||
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_int32();
|
||||
size_t nsteps = sb->read_size();
|
||||
version_number_ = sb->read_int64();
|
||||
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];
|
||||
@@ -349,15 +371,22 @@ bool AnimQueue::make_patch(const AnimQueue &auth, StreamBuffer *sb) const {
|
||||
// Sanity check.
|
||||
assert(valid());
|
||||
assert(auth.valid());
|
||||
|
||||
// Fast path to detect equivalence.
|
||||
if (version_number_ == auth.version_number_) {
|
||||
sb->write_uint8(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Special case: if we're already a perfect match, output 0 bytes.
|
||||
if (exactly_equal(auth)) {
|
||||
// Special case: if we're already a perfect match.
|
||||
if (size_and_steps_equal(auth)) {
|
||||
sb->write_uint8(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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))) {
|
||||
@@ -401,7 +430,7 @@ void AnimQueue::apply_patch(StreamBuffer *sb) {
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_limit_ = sb->read_uint8();
|
||||
// Decode the diff, stop at eof.
|
||||
std::deque<AnimStep> old = std::move(steps_);
|
||||
steps_.clear();
|
||||
@@ -422,6 +451,12 @@ void AnimQueue::apply_patch(StreamBuffer *sb) {
|
||||
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 {
|
||||
@@ -431,9 +466,9 @@ const AnimStep &AnimQueue::back() const {
|
||||
LuaDefine(unittests_animqueue, "c") {
|
||||
// Check initial state.
|
||||
AnimStep stp;
|
||||
AnimQueue aq;
|
||||
AnimQueue aq(util::WORLD_TYPE_MASTER);
|
||||
StreamBuffer sb;
|
||||
AnimQueue aqds;
|
||||
AnimQueue aqds(util::WORLD_TYPE_S_SYNC);
|
||||
aq.set_size_limit(3);
|
||||
|
||||
LuaAssert(L, aq.valid());
|
||||
@@ -568,7 +603,7 @@ LuaDefine(unittests_animqueue, "c") {
|
||||
sb.clear();
|
||||
LuaAssert(L, aqds.make_patch(aq, &sb));
|
||||
aqds.apply_patch(&sb);
|
||||
LuaAssert(L, aqds.exactly_equal(aq));
|
||||
LuaAssert(L, aqds.size_and_steps_equal(aq));
|
||||
|
||||
// Add another action.
|
||||
stp.clear();
|
||||
@@ -585,21 +620,31 @@ LuaDefine(unittests_animqueue, "c") {
|
||||
// Apply the diffs, 4 extra bytes should remain.
|
||||
aqds.apply_patch(&sb);
|
||||
LuaAssert(L, sb.write_count() - sb.read_count() == 4);
|
||||
LuaAssert(L, aqds.exactly_equal(aq));
|
||||
LuaAssert(L, aqds.size_and_steps_equal(aq));
|
||||
|
||||
// Change the queue size limit.
|
||||
aq.set_size_limit(13);
|
||||
|
||||
// Generate diffs, but add 4 extra bytes.
|
||||
sb.clear();
|
||||
LuaAssert(L, !aqds.size_and_steps_equal(aq));
|
||||
LuaAssert(L, aqds.make_patch(aq, &sb));
|
||||
aqds.apply_patch(&sb);
|
||||
LuaAssert(L, aqds.size_and_steps_equal(aq));
|
||||
|
||||
// compare again, should be no differences.
|
||||
sb.clear();
|
||||
LuaAssert(L, !aqds.make_patch(aq, &sb));
|
||||
aqds.apply_patch(&sb);
|
||||
LuaAssert(L, aqds.exactly_equal(aq));
|
||||
LuaAssert(L, aqds.size_and_steps_equal(aq));
|
||||
|
||||
// Discard all but the last action.
|
||||
aq.keep_only(1);
|
||||
aq.set_size_limit(1);
|
||||
|
||||
sb.clear();
|
||||
LuaAssert(L, aqds.make_patch(aq, &sb));
|
||||
aqds.apply_patch(&sb);
|
||||
LuaAssert(L, aqds.exactly_equal(aq));
|
||||
LuaAssert(L, aqds.size_and_steps_equal(aq));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user