Overhaul animation queues so the queue is stored as a single encoded string.
This commit is contained in:
@@ -9,6 +9,9 @@
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
util::SharedStdString make_shared_string(const StreamBuffer &sb) {
|
||||
return std::make_shared<std::string>(sb.view());
|
||||
}
|
||||
|
||||
static const char *vtname(AnimValueType vt) {
|
||||
switch (vt) {
|
||||
@@ -22,7 +25,7 @@ static const char *vtname(AnimValueType vt) {
|
||||
}
|
||||
|
||||
|
||||
uint64_t hash_encoding(uint64_t prev, std::string_view s) {
|
||||
uint64_t hash_encstep(uint64_t prev, std::string_view s) {
|
||||
return util::hash_string(util::HashValue(123, prev), s).first;
|
||||
}
|
||||
|
||||
@@ -130,6 +133,31 @@ static void parse_value(std::string_view vstr, AnimValue *v) {
|
||||
v->set_string(vstr);
|
||||
}
|
||||
|
||||
|
||||
static AnimValue parse_anim_value(LuaCoreStack &LS, LuaSlot val, LuaSlot tmp) {
|
||||
AnimValue result;
|
||||
if (LS.isboolean(val)) {
|
||||
result.set_boolean(LS.ckboolean(val));
|
||||
} else if (LS.isnumber(val)) {
|
||||
result.set_number(LS.cknumber(val));
|
||||
} else if (LS.isstring(val)) {
|
||||
result.set_string(LS.ckstring(val));
|
||||
} else if (LS.istable(val)) {
|
||||
util::DXYZ xyz;
|
||||
LS.rawget(tmp, val, 1);
|
||||
if (!LS.isnumber(tmp)) return result;
|
||||
xyz.x = LS.cknumber(tmp);
|
||||
LS.rawget(tmp, val, 2);
|
||||
if (!LS.isnumber(tmp)) return result;
|
||||
xyz.y = LS.cknumber(tmp);
|
||||
LS.rawget(tmp, val, 3);
|
||||
if (!LS.isnumber(tmp)) return result;
|
||||
xyz.z = LS.cknumber(tmp);
|
||||
result.set_xyz(xyz);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void AnimState::set_persistent(const eng::string &name) {
|
||||
map_[name].persistent = true;
|
||||
}
|
||||
@@ -303,31 +331,6 @@ eng::string AnimState::add_defaults(const AnimState *other) {
|
||||
}
|
||||
|
||||
|
||||
static AnimValue parse_anim_value(LuaCoreStack &LS, LuaSlot val, LuaSlot tmp) {
|
||||
AnimValue result;
|
||||
if (LS.isboolean(val)) {
|
||||
result.set_boolean(LS.ckboolean(val));
|
||||
} else if (LS.isnumber(val)) {
|
||||
result.set_number(LS.cknumber(val));
|
||||
} else if (LS.isstring(val)) {
|
||||
result.set_string(LS.ckstring(val));
|
||||
} else if (LS.istable(val)) {
|
||||
util::DXYZ xyz;
|
||||
LS.rawget(tmp, val, 1);
|
||||
if (!LS.isnumber(tmp)) return result;
|
||||
xyz.x = LS.cknumber(tmp);
|
||||
LS.rawget(tmp, val, 2);
|
||||
if (!LS.isnumber(tmp)) return result;
|
||||
xyz.y = LS.cknumber(tmp);
|
||||
LS.rawget(tmp, val, 3);
|
||||
if (!LS.isnumber(tmp)) return result;
|
||||
xyz.z = LS.cknumber(tmp);
|
||||
result.set_xyz(xyz);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
eng::string AnimState::apply_lua(LuaCoreStack &LS0, LuaSlot tab, bool setpersist) {
|
||||
LuaVar key, val, tmp;
|
||||
LuaExtStack LS(LS0.state(), key, val, tmp);
|
||||
@@ -420,6 +423,54 @@ void AnimCoreState::decode(std::string_view s) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct StepBreakout {
|
||||
uint64_t hash;
|
||||
std::string_view encstep;
|
||||
StepBreakout(uint64_t h, std::string_view e) : hash(h), encstep(e) {}
|
||||
};
|
||||
|
||||
using StepBreakoutVec = eng::vector<StepBreakout>;
|
||||
|
||||
StepBreakoutVec encqueue_breakout(std::string_view encqueue) {
|
||||
StepBreakoutVec result;
|
||||
StreamBuffer sb(encqueue);
|
||||
while (!sb.empty()) {
|
||||
uint64_t hash = sb.read_uint64();
|
||||
std::string_view encstep = sb.read_string_view();
|
||||
result.emplace_back(hash, encstep);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Just return the hash of the very last step. (Steps are stored last to first).
|
||||
static uint64_t encqueue_final_hash(std::string_view encqueue) {
|
||||
StreamBuffer sb(encqueue);
|
||||
uint64_t hash = sb.read_uint64();
|
||||
return hash;
|
||||
}
|
||||
|
||||
static std::string_view encqueue_final_encstep(std::string_view encqueue) {
|
||||
StreamBuffer sb(encqueue);
|
||||
sb.read_uint64();
|
||||
std::string_view result = sb.read_string_view();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Return the encqueue for the first N steps. If there aren't that
|
||||
// many steps, then just return them all.
|
||||
std::string_view encqueue_firstn(std::string_view encqueue, int n) {
|
||||
StreamBuffer sb(encqueue);
|
||||
while ((n > 0) && (!sb.empty())) {
|
||||
sb.read_uint64();
|
||||
uint64_t slen = sb.read_length();
|
||||
sb.read_bytes(slen);
|
||||
n -= 1;
|
||||
}
|
||||
return encqueue.substr(0, sb.total_reads());
|
||||
}
|
||||
|
||||
|
||||
AnimQueue::AnimQueue() {
|
||||
size_limit_ = 10; // Default size limit.
|
||||
clear();
|
||||
@@ -431,53 +482,39 @@ void AnimQueue::clear() {
|
||||
}
|
||||
|
||||
void AnimQueue::clear(const AnimState &state) {
|
||||
steps_.clear();
|
||||
eng::string encoding = state.encode();
|
||||
uint64_t hash = hash_encoding(0, encoding);
|
||||
steps_.emplace_back(encoding, hash);
|
||||
StreamBuffer result;
|
||||
eng::string encstep = state.encode();
|
||||
uint64_t hash = hash_encstep(0, encstep);
|
||||
result.write_uint64(hash);
|
||||
result.write_string(encstep);
|
||||
encqueue_ = make_shared_string(result);
|
||||
}
|
||||
|
||||
void AnimQueue::set_limit(int n) {
|
||||
assert((n >= 2) && (n <= 250));
|
||||
size_limit_ = n;
|
||||
if (int(steps_.size()) > n) {
|
||||
while (int(steps_.size()) > n) {
|
||||
steps_.pop_front();
|
||||
}
|
||||
}
|
||||
void AnimQueue::set_limit(int nkeep) {
|
||||
assert((nkeep >= 2) && (nkeep <= 250));
|
||||
size_limit_ = nkeep;
|
||||
encqueue_ = std::make_shared<std::string>(encqueue_firstn(*encqueue_, nkeep));
|
||||
}
|
||||
|
||||
void AnimQueue::add(const AnimState &state) {
|
||||
eng::string encoding = state.encode();
|
||||
uint64_t hash = hash_encoding(steps_.back().hash, encoding);
|
||||
steps_.emplace_back(encoding, hash);
|
||||
set_limit(size_limit_);
|
||||
uint64_t previoushash = encqueue_final_hash(*encqueue_);
|
||||
eng::string encstep = state.encode();
|
||||
uint64_t hash = hash_encstep(previoushash, encstep);
|
||||
StreamBuffer result;
|
||||
result.write_uint64(hash);
|
||||
result.write_string(encstep);
|
||||
result.write_bytes(encqueue_firstn(*encqueue_, size_limit_ - 1));
|
||||
encqueue_ = make_shared_string(result);
|
||||
}
|
||||
|
||||
void AnimQueue::serialize(StreamBuffer *sb) const {
|
||||
sb->write_uint8(size_limit_);
|
||||
sb->write_uint8(steps_.size());
|
||||
sb->write_uint64(steps_[0].hash);
|
||||
for (const AnimQueue::Step &step : steps_) {
|
||||
sb->write_string(step.encoding);
|
||||
}
|
||||
sb->write_string(*encqueue_);
|
||||
}
|
||||
|
||||
void AnimQueue::deserialize(StreamBuffer *sb) {
|
||||
size_limit_ = sb->read_uint8();
|
||||
int nsteps = sb->read_uint8();
|
||||
uint64_t firsthash = sb->read_uint64();
|
||||
assert ((nsteps >= 2) && (nsteps <= size_limit_) && (size_limit_ >= 2) && (size_limit_ <= 250));
|
||||
steps_.resize(nsteps);
|
||||
for (int i = 0; i < nsteps; i++) {
|
||||
AnimQueue::Step &step = steps_[i];
|
||||
step.encoding = sb->read_string();
|
||||
if (i == 0) {
|
||||
step.hash = firsthash;
|
||||
} else {
|
||||
step.hash = hash_encoding(steps_[i-1].hash, step.encoding);
|
||||
}
|
||||
}
|
||||
encqueue_ = std::make_shared<std::string>(sb->read_string_view());
|
||||
}
|
||||
|
||||
bool AnimQueue::diff(const AnimQueue &auth, StreamBuffer *sb) const {
|
||||
@@ -488,28 +525,10 @@ bool AnimQueue::diff(const AnimQueue &auth, StreamBuffer *sb) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(auth.steps_.size() < 255);
|
||||
sb->write_uint8(auth.steps_.size());
|
||||
sb->write_uint8(auth.size_limit_);
|
||||
sb->write_uint64(auth.steps_.front().hash);
|
||||
|
||||
// Index all known elements by text.
|
||||
eng::map<eng::string, int> index;
|
||||
for (int i = 0; i < int(steps_.size()); i++) {
|
||||
index[steps_[i].encoding] = i;
|
||||
}
|
||||
|
||||
// Write the encoded elements.
|
||||
for (int i = 0; i < int(auth.steps_.size()); i++) {
|
||||
const AnimQueue::Step &step = auth.steps_[i];
|
||||
auto iter = index.find(step.encoding);
|
||||
if (iter == index.end()) {
|
||||
sb->write_uint8(255);
|
||||
sb->write_string(step.encoding);
|
||||
} else {
|
||||
sb->write_uint8(iter->second);
|
||||
}
|
||||
}
|
||||
// TODO: maybe send less data?
|
||||
sb->write_uint8(0);
|
||||
sb->write_uint32(auth.size_limit_);
|
||||
sb->write_string(*auth.encqueue_);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -520,68 +539,46 @@ void AnimQueue::patch(StreamBuffer *sb, DebugCollector *dbc) {
|
||||
}
|
||||
DebugLine(dbc) << "AnimQueue modified";
|
||||
|
||||
size_limit_ = sb->read_uint8();
|
||||
uint64_t firsthash = sb->read_uint64();
|
||||
|
||||
assert ((nsteps >= 2) && (nsteps <= size_limit_) && (size_limit_ >= 2) && (size_limit_ <= 250));
|
||||
|
||||
// Decode the diff, stop at eof.
|
||||
eng::deque<AnimQueue::Step> old = std::move(steps_);
|
||||
steps_.resize(nsteps);
|
||||
for (int i = 0; i < nsteps; i++) {
|
||||
AnimQueue::Step &step = steps_[i];
|
||||
uint8_t index = sb->read_uint8();
|
||||
if (index < 255) {
|
||||
assert(index < old.size());
|
||||
step.encoding = old[index].encoding;
|
||||
} else {
|
||||
step.encoding = sb->read_string();
|
||||
}
|
||||
if (i == 0) {
|
||||
step.hash = firsthash;
|
||||
} else {
|
||||
step.hash = hash_encoding(steps_[i-1].hash, step.encoding);
|
||||
}
|
||||
}
|
||||
size_limit_ = sb->read_uint32();
|
||||
std::string_view steps = sb->read_string_view();
|
||||
encqueue_ = std::make_shared<std::string>(steps);
|
||||
}
|
||||
|
||||
bool AnimQueue::exactly_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++) {
|
||||
const AnimQueue::Step &step = steps_[i];
|
||||
const AnimQueue::Step &otherstep = other.steps_[i];
|
||||
if (step.hash != otherstep.hash) return false;
|
||||
if (step.encoding != otherstep.encoding) return false;
|
||||
}
|
||||
if (*encqueue_ != *other.encqueue_) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnimQueue::exactly_equal_fast(const AnimQueue &other) const {
|
||||
if (size_limit_ != other.size_limit_) return false;
|
||||
if (steps_.size() != other.steps_.size()) return false;
|
||||
if (steps_.back().hash != other.steps_.back().hash) return false;
|
||||
if (encqueue_->size() != other.encqueue_->size()) return false;
|
||||
if (encqueue_->compare(0, 8, *other.encqueue_) != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
eng::string AnimQueue::steps_debug_string() const {
|
||||
StepBreakoutVec breakout = encqueue_breakout(*encqueue_);
|
||||
eng::ostringstream oss;
|
||||
bool first = true;
|
||||
for (const AnimQueue::Step &step : steps_) {
|
||||
for (int i = breakout.size() - 1; i >= 0; i--) {
|
||||
const StepBreakout &step = breakout[i];
|
||||
if (!first) oss << "; ";
|
||||
first = false;
|
||||
AnimState state(step.encoding);
|
||||
AnimState state(step.encstep);
|
||||
state.print_debug_string(oss);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
eng::string AnimQueue::full_debug_string() const {
|
||||
StepBreakoutVec breakout = encqueue_breakout(*encqueue_);
|
||||
eng::ostringstream oss;
|
||||
oss << "limit=" << size_limit();
|
||||
for (const AnimQueue::Step &step : steps_) {
|
||||
for (int i = breakout.size() - 1; i >= 0; i--) {
|
||||
const StepBreakout &step = breakout[i];
|
||||
oss << "; ";
|
||||
AnimState state(step.encoding);
|
||||
AnimState state(step.encstep);
|
||||
state.print_debug_string(oss);
|
||||
}
|
||||
return oss.str();
|
||||
@@ -590,38 +587,30 @@ eng::string AnimQueue::full_debug_string() const {
|
||||
// Get the final entry, xyz and plane only.
|
||||
//
|
||||
AnimCoreState AnimQueue::get_final_core_state() const {
|
||||
std::string_view encstep = encqueue_final_encstep(*encqueue_);
|
||||
AnimCoreState result;
|
||||
result.decode(steps_.back().encoding);
|
||||
result.decode(encstep);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get the final entry, all persistent variables.
|
||||
//
|
||||
AnimState AnimQueue::get_final_persistent() const {
|
||||
std::string_view encstep = encqueue_final_encstep(*encqueue_);
|
||||
AnimState result;
|
||||
result.decode_persistent(steps_.back().encoding);
|
||||
result.decode_persistent(encstep);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get the final entry, everything persistent and non-persistent.
|
||||
//
|
||||
AnimState AnimQueue::get_final_everything() const {
|
||||
std::string_view encstep = encqueue_final_encstep(*encqueue_);
|
||||
AnimState result;
|
||||
result.decode(steps_.back().encoding);
|
||||
result.decode(encstep);
|
||||
return result;
|
||||
}
|
||||
|
||||
void AnimQueue::get_for_engine_wrapper(std::vector<EngineWrapper::AnimEntry> *into) const {
|
||||
into->resize(steps_.size());
|
||||
for (int i = 0; i < int(steps_.size()); i++) {
|
||||
const AnimQueue::Step &step = steps_[i];
|
||||
EngineWrapper::AnimEntry &entry = (*into)[i];
|
||||
entry.hash = step.hash;
|
||||
entry.data = step.encoding.c_str();
|
||||
entry.size = step.encoding.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LuaDefine(unittests_animqueue, "", "some unit tests") {
|
||||
// Useful objects.
|
||||
@@ -757,7 +746,7 @@ LuaDefine(unittests_animqueue, "", "some unit tests") {
|
||||
aqs.clear();
|
||||
sb.clear();
|
||||
aqs.diff(aq, &sb);
|
||||
int difflen1 = sb.fill();
|
||||
//int difflen1 = sb.fill();
|
||||
LuaAssert(L, !aqs.exactly_equal(aq));
|
||||
aqs.patch(&sb, nullptr);
|
||||
LuaAssert(L, aqs.exactly_equal(aq));
|
||||
@@ -775,12 +764,14 @@ LuaDefine(unittests_animqueue, "", "some unit tests") {
|
||||
LuaAssertStrEq(L, aqs.full_debug_string(), "limit=10; action:jump xyz=4,5,6; plane=earth xyz=1,2,3; airline:southwest plane=moon");
|
||||
sb.clear();
|
||||
aqs.diff(aq, &sb);
|
||||
int difflen2 = sb.fill();
|
||||
//int difflen2 = sb.fill();
|
||||
LuaAssert(L, !aqs.exactly_equal(aq));
|
||||
aqs.patch(&sb, nullptr);
|
||||
LuaAssert(L, aqs.exactly_equal(aq));
|
||||
LuaAssertStrEq(L, aqs.full_debug_string(), "limit=10; plane=earth xyz=1,2,3; action:jump xyz=4,5,6; airline:southwest plane=moon; color=blue");
|
||||
LuaAssert(L, difflen2 < (difflen1 / 2));
|
||||
|
||||
// TODO: if we make the diff routine more efficient, this should be true.
|
||||
// LuaAssert(L, difflen2 < (difflen1 / 2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user