diff xmit of animation queues. Also, StreamBuffer::unwrite_to
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
#include "luastack.hpp"
|
#include "luastack.hpp"
|
||||||
#include "animqueue.hpp"
|
#include "animqueue.hpp"
|
||||||
|
|
||||||
@@ -8,6 +9,8 @@ AnimStep::AnimStep() {
|
|||||||
|
|
||||||
AnimStep::~AnimStep() {}
|
AnimStep::~AnimStep() {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AnimStep::clear() {
|
void AnimStep::clear() {
|
||||||
id_ = 0;
|
id_ = 0;
|
||||||
bits_ = 0;
|
bits_ = 0;
|
||||||
@@ -57,11 +60,88 @@ void AnimStep::set_plane(const std::string &p) {
|
|||||||
plane_ = p;
|
plane_ = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimQueue::AnimQueue() {
|
|
||||||
size_limit_ = 10; // Default size limit.
|
bool AnimStep::exactly_equal(const AnimStep &other) const {
|
||||||
clear_steps();
|
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) {
|
void AnimStep::from_lua_store_string(lua_State *L, int idx, std::string *target, int16_t bits, const char *name) {
|
||||||
if ((bits_ & bits)||(*target != "")) {
|
if ((bits_ & bits)||(*target != "")) {
|
||||||
luaL_error(L, "specified %s twice", name);
|
luaL_error(L, "specified %s twice", name);
|
||||||
@@ -123,6 +203,12 @@ void AnimStep::from_lua(lua_State *L, int idx, const AnimStep &qback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimStep::keep_state_only() {
|
||||||
|
bits_ = HAS_EVERYTHING;
|
||||||
|
id_ = 0;
|
||||||
|
action_ = "";
|
||||||
|
}
|
||||||
|
|
||||||
void AnimStep::echo(const AnimStep &prev) {
|
void AnimStep::echo(const AnimStep &prev) {
|
||||||
if (!has_facing()) {
|
if (!has_facing()) {
|
||||||
facing_ = prev.facing_;
|
facing_ = prev.facing_;
|
||||||
@@ -166,6 +252,23 @@ bool AnimStep::echoes(const AnimStep &prev) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AnimQueue::AnimQueue() {
|
||||||
|
size_limit_ = 10; // Default size limit.
|
||||||
|
clear_steps();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimQueue::exactly_equal(const AnimQueue &other) const {
|
||||||
|
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::clear_steps() {
|
void AnimQueue::clear_steps() {
|
||||||
steps_.clear();
|
steps_.clear();
|
||||||
steps_.emplace_back();
|
steps_.emplace_back();
|
||||||
@@ -186,10 +289,7 @@ void AnimQueue::keep_only(int n) {
|
|||||||
while (int(steps_.size()) > n) {
|
while (int(steps_.size()) > n) {
|
||||||
steps_.pop_front();
|
steps_.pop_front();
|
||||||
}
|
}
|
||||||
AnimStep &init = steps_.front();
|
steps_.front().keep_state_only();
|
||||||
init.id_ = 0;
|
|
||||||
init.action_ = "";
|
|
||||||
init.bits_ = AnimStep::HAS_EVERYTHING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimQueue::add(int64_t id, const AnimStep &step) {
|
void AnimQueue::add(int64_t id, const AnimStep &step) {
|
||||||
@@ -200,7 +300,7 @@ void AnimQueue::add(int64_t id, const AnimStep &step) {
|
|||||||
keep_only(size_limit_);
|
keep_only(size_limit_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimQueue::valid() {
|
bool AnimQueue::valid() const {
|
||||||
// Animqueue must have at least one step.
|
// Animqueue must have at least one step.
|
||||||
if (steps_.empty()) {
|
if (steps_.empty()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -227,27 +327,7 @@ void AnimQueue::serialize(StreamBuffer *sb) {
|
|||||||
sb->write_int32(size_limit_);
|
sb->write_int32(size_limit_);
|
||||||
sb->write_size(steps_.size());
|
sb->write_size(steps_.size());
|
||||||
for (const AnimStep &step : steps_) {
|
for (const AnimStep &step : steps_) {
|
||||||
sb->write_int64(step.id_);
|
step.write_into(sb);
|
||||||
sb->write_int16(step.bits_);
|
|
||||||
sb->write_string(step.action_);
|
|
||||||
if (step.has_facing()) {
|
|
||||||
sb->write_float(step.facing_);
|
|
||||||
}
|
|
||||||
if (step.has_x()) {
|
|
||||||
sb->write_float(step.xyz_.x);
|
|
||||||
}
|
|
||||||
if (step.has_y()) {
|
|
||||||
sb->write_float(step.xyz_.y);
|
|
||||||
}
|
|
||||||
if (step.has_z()) {
|
|
||||||
sb->write_float(step.xyz_.z);
|
|
||||||
}
|
|
||||||
if (step.has_graphic()) {
|
|
||||||
sb->write_string(step.graphic_);
|
|
||||||
}
|
|
||||||
if (step.has_plane()) {
|
|
||||||
sb->write_string(step.plane_);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,29 +337,84 @@ void AnimQueue::deserialize(StreamBuffer *sb) {
|
|||||||
steps_.resize(nsteps);
|
steps_.resize(nsteps);
|
||||||
for (size_t i = 0; i < nsteps; i++) {
|
for (size_t i = 0; i < nsteps; i++) {
|
||||||
AnimStep &step = steps_[i];
|
AnimStep &step = steps_[i];
|
||||||
if (i > 0) step = steps_[i - 1];
|
step.read_from(sb);
|
||||||
step.id_ = sb->read_int64();
|
if (i > 0) step.echo(steps_[i - 1]);
|
||||||
step.bits_ = sb->read_int16();
|
|
||||||
step.action_ = sb->read_string();
|
|
||||||
if (step.has_facing()) {
|
|
||||||
step.facing_ = sb->read_float();
|
|
||||||
}
|
}
|
||||||
if (step.has_x()) {
|
}
|
||||||
step.xyz_.x = sb->read_float();
|
|
||||||
|
void AnimQueue::make_patch(const AnimQueue &auth, StreamBuffer *sb) const {
|
||||||
|
// Sanity check.
|
||||||
|
assert(valid());
|
||||||
|
assert(auth.valid());
|
||||||
|
|
||||||
|
// Special case: if we're already a perfect match, output 0 bytes.
|
||||||
|
if (exactly_equal(auth)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (step.has_y()) {
|
|
||||||
step.xyz_.y = sb->read_float();
|
// Write the first element.
|
||||||
|
const AnimStep &first = auth.steps_[0];
|
||||||
|
int match = 0;
|
||||||
|
while ((match < int(steps_.size())) && (!steps_[match].state_equal(first))) {
|
||||||
|
match++;
|
||||||
}
|
}
|
||||||
if (step.has_z()) {
|
if (match == int(steps_.size())) {
|
||||||
step.xyz_.z = sb->read_float();
|
sb->write_uint8(255);
|
||||||
|
first.write_into(sb);
|
||||||
|
} else {
|
||||||
|
sb->write_uint8(match);
|
||||||
}
|
}
|
||||||
if (step.has_graphic()) {
|
|
||||||
step.graphic_ = sb->read_string();
|
// 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;
|
||||||
}
|
}
|
||||||
if (step.has_plane()) {
|
|
||||||
step.plane_ = sb->read_string();
|
// 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::apply_patch(StreamBuffer *sb) {
|
||||||
|
// Special case: if there are zero bytes, no patch is needed.
|
||||||
|
if (sb->at_eof()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the diff, stop at eof.
|
||||||
|
std::deque<AnimStep> old = std::move(steps_);
|
||||||
|
steps_.clear();
|
||||||
|
while (!sb->at_eof()) {
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps_.front().keep_state_only();
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimStep &AnimQueue::back() const {
|
const AnimStep &AnimQueue::back() const {
|
||||||
@@ -411,5 +546,42 @@ LuaDefine(unittests_animqueue, "c") {
|
|||||||
LuaAssert(L, aqds.nth(3).graphic() == "banana");
|
LuaAssert(L, aqds.nth(3).graphic() == "banana");
|
||||||
LuaAssert(L, aqds.nth(3).plane() == "");
|
LuaAssert(L, aqds.nth(3).plane() == "");
|
||||||
|
|
||||||
|
// Test difference transmission
|
||||||
|
// Start with an anim queue with an initial state and a single action.
|
||||||
|
aq.set_size_limit(10);
|
||||||
|
aqds.set_size_limit(10);
|
||||||
|
aq.clear_steps();
|
||||||
|
aqds.clear_steps();
|
||||||
|
|
||||||
|
stp.clear();
|
||||||
|
stp.set_action("walk");
|
||||||
|
stp.set_xyz(util::XYZ(3,4,5));
|
||||||
|
aq.add(12345, stp);
|
||||||
|
|
||||||
|
sb.clear();
|
||||||
|
aqds.make_patch(aq, &sb);
|
||||||
|
aqds.apply_patch(&sb);
|
||||||
|
LuaAssert(L, aqds.exactly_equal(aq));
|
||||||
|
|
||||||
|
// Add another action.
|
||||||
|
stp.clear();
|
||||||
|
stp.set_action("fnord");
|
||||||
|
stp.set_facing(123);
|
||||||
|
stp.set_plane("where");
|
||||||
|
aq.add(232, stp);
|
||||||
|
|
||||||
|
sb.clear();
|
||||||
|
aqds.make_patch(aq, &sb);
|
||||||
|
aqds.apply_patch(&sb);
|
||||||
|
LuaAssert(L, aqds.exactly_equal(aq));
|
||||||
|
|
||||||
|
// Discard all but the last action.
|
||||||
|
aq.keep_only(1);
|
||||||
|
|
||||||
|
sb.clear();
|
||||||
|
aqds.make_patch(aq, &sb);
|
||||||
|
aqds.apply_patch(&sb);
|
||||||
|
LuaAssert(L, aqds.exactly_equal(aq));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,20 @@ public:
|
|||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
// ExactlyEqual compares all fields.
|
||||||
|
bool exactly_equal(const AnimStep &other) const;
|
||||||
|
|
||||||
|
// LogicallyEqual only compares fields whose HAS_XXX bits are set.
|
||||||
|
bool logically_equal(const AnimStep &other) const;
|
||||||
|
|
||||||
|
// StateEqual is true if the plane, graphic, facing, and xyz match.
|
||||||
|
bool state_equal(const AnimStep &other) const;
|
||||||
|
|
||||||
|
// read/write the step using a stream buffer.
|
||||||
|
//
|
||||||
|
void write_into(StreamBuffer *sb) const;
|
||||||
|
void read_from(StreamBuffer *sb);
|
||||||
|
|
||||||
// Create an AnimStep from a lua table.
|
// Create an AnimStep from a lua table.
|
||||||
//
|
//
|
||||||
// Lua stack must contain a table, which may contain:
|
// Lua stack must contain a table, which may contain:
|
||||||
@@ -111,6 +125,9 @@ public:
|
|||||||
//
|
//
|
||||||
void from_lua(lua_State *L, int idx, const AnimStep &qback);
|
void from_lua(lua_State *L, int idx, const AnimStep &qback);
|
||||||
|
|
||||||
|
// Make this step into a first-step of an anim queue.
|
||||||
|
void keep_state_only();
|
||||||
|
|
||||||
// For any values that are unchanged in this step,
|
// For any values that are unchanged in this step,
|
||||||
// echo the values of the previous step.
|
// echo the values of the previous step.
|
||||||
void echo(const AnimStep &prev);
|
void echo(const AnimStep &prev);
|
||||||
@@ -127,6 +144,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
AnimQueue();
|
AnimQueue();
|
||||||
|
|
||||||
|
bool exactly_equal(const AnimQueue &aq) const;
|
||||||
|
|
||||||
const AnimStep &nth(int n) const { return steps_[n]; }
|
const AnimStep &nth(int n) const { return steps_[n]; }
|
||||||
size_t size() const { return steps_.size(); }
|
size_t size() const { return steps_.size(); }
|
||||||
int32_t size_limit() const { return size_limit_; }
|
int32_t size_limit() const { return size_limit_; }
|
||||||
@@ -147,6 +166,15 @@ public:
|
|||||||
void serialize(StreamBuffer *sb);
|
void serialize(StreamBuffer *sb);
|
||||||
void deserialize(StreamBuffer *sb);
|
void deserialize(StreamBuffer *sb);
|
||||||
|
|
||||||
|
// Difference transmission
|
||||||
|
//
|
||||||
|
// make_patch will emit zero bytes when there are no differences.
|
||||||
|
// make_patch does not put a 'length' field for the patch as a whole.
|
||||||
|
// apply_patch must receive a patch followed by eof.
|
||||||
|
//
|
||||||
|
void make_patch(const AnimQueue &auth, StreamBuffer *sb) const;
|
||||||
|
void apply_patch(StreamBuffer *sb);
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
||||||
@@ -154,7 +182,7 @@ public:
|
|||||||
void set_size_limit(int32_t n);
|
void set_size_limit(int32_t n);
|
||||||
|
|
||||||
// (For testing): make sure the invariants are preserved.
|
// (For testing): make sure the invariants are preserved.
|
||||||
bool valid();
|
bool valid() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ANIMQUEUE_HPP
|
#endif // ANIMQUEUE_HPP
|
||||||
|
|||||||
@@ -290,6 +290,12 @@ void StreamBuffer::unread_to(int64_t rd_count) {
|
|||||||
read_cursor_ = buf_lo_ + (rd_count - pre_read_count_);
|
read_cursor_ = buf_lo_ + (rd_count - pre_read_count_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StreamBuffer::unwrite_to(int64_t wr_count) {
|
||||||
|
assert(wr_count >= read_count());
|
||||||
|
assert(wr_count <= write_count());
|
||||||
|
write_cursor_ = buf_lo_ + (wr_count - pre_read_count_);
|
||||||
|
}
|
||||||
|
|
||||||
int StreamBuffer::lua_writer(lua_State *L, const void* p, size_t sz, void* ud) {
|
int StreamBuffer::lua_writer(lua_State *L, const void* p, size_t sz, void* ud) {
|
||||||
StreamBuffer *sb = (StreamBuffer *)ud;
|
StreamBuffer *sb = (StreamBuffer *)ud;
|
||||||
sb->write_bytes((const char *)p, sz);
|
sb->write_bytes((const char *)p, sz);
|
||||||
|
|||||||
@@ -372,6 +372,9 @@ public:
|
|||||||
// Rewind the read cursor to a previous position.
|
// Rewind the read cursor to a previous position.
|
||||||
void unread_to(int64_t read_count);
|
void unread_to(int64_t read_count);
|
||||||
|
|
||||||
|
// Rewind the write cursor to a previous position.
|
||||||
|
void unwrite_to(int64_t write_count);
|
||||||
|
|
||||||
// Use the stream buffer as a lua_Writer.
|
// Use the stream buffer as a lua_Writer.
|
||||||
static int lua_writer(lua_State *L, const void* p, size_t sz, void* ud);
|
static int lua_writer(lua_State *L, const void* p, size_t sz, void* ud);
|
||||||
void *lua_writer_ud();
|
void *lua_writer_ud();
|
||||||
|
|||||||
Reference in New Issue
Block a user