From b459eedc822b8172c7c5b0a84132a4c6bc73b20b Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 26 Jul 2023 17:40:20 -0400 Subject: [PATCH] Overhaul animation queues so the queue is stored as a single encoded string. --- luprex/cpp/core/animqueue.cpp | 261 +++++++++++++++--------------- luprex/cpp/core/animqueue.hpp | 87 +++++++--- luprex/cpp/core/drivenengine.cpp | 41 ++--- luprex/cpp/core/drivenengine.hpp | 7 +- luprex/cpp/core/enginewrapper.hpp | 36 +---- luprex/cpp/core/streambuffer.cpp | 9 ++ luprex/cpp/core/streambuffer.hpp | 7 +- luprex/cpp/core/util.hpp | 2 + luprex/cpp/core/world-core.cpp | 17 +- luprex/cpp/core/world.hpp | 14 +- 10 files changed, 239 insertions(+), 242 deletions(-) diff --git a/luprex/cpp/core/animqueue.cpp b/luprex/cpp/core/animqueue.cpp index fd05f1e7..65693017 100644 --- a/luprex/cpp/core/animqueue.cpp +++ b/luprex/cpp/core/animqueue.cpp @@ -9,6 +9,9 @@ #include #include +util::SharedStdString make_shared_string(const StreamBuffer &sb) { + return std::make_shared(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; + +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(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(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 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 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(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 *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; } diff --git a/luprex/cpp/core/animqueue.hpp b/luprex/cpp/core/animqueue.hpp index 931d8edf..c8cd95cc 100644 --- a/luprex/cpp/core/animqueue.hpp +++ b/luprex/cpp/core/animqueue.hpp @@ -18,14 +18,63 @@ // Persistent values are retained from one animation step to the next, // nonpersistent values exist for one animation step only. // -// Animation steps are stored encoded as strings, which is convenient for -// passing the data to unreal, but it means that the animation step has to be -// decoded whenever you want access to the key-value pairs. -// // Each animation step has a hash value. The hash value is generated // by mixing the hash value of the previous step with the hash value // of the encoded string of key-value pairs. // +// +/////////////////////////////////////////////////////////////////// +// +// SERIALIZED STORAGE +// +// The entired animation queue is stored in a serialized format, +// as a shared string. This means that the animation queue can be +// passed to the graphics engine as a single string. This vastly +// simplifies the API for passing the animation queue to the +// graphics engine. It also vastly reduces the amount of computation +// done during the graphics engine probe: all we have to do is get +// the already existing string and pass a reference to it. +// +// However, that means that when manipulating the animation queue, +// we have to decode it, manipulate it, and then reencode it. This +// is a good tradeoff: we update animation queues rarely, compared +// to how often we pass them to the graphics engine. +// +// So first, you need to know how to serialize a single animation +// step. Remember, an animation step consists of a list of key-value +// pairs (see above). The key-value pairs are serialized as follows: +// +// for all key-value pairs do: +// write_string(key) +// write_bool(persistent) +// write_uint8(type) // T_STRING, T_NUMBER, T_BOOL, or T_XYZ +// switch type: +// T_STRING: write_string(value) +// T_NUMBER: write_double(value) +// T_BOOL: write_bool(value) +// T_XYZ: write_xyz(value) +// +// The encoded string produced by the loop above is called an "encstep". +// That's short for "encoded animation step". The encstep has a hash +// value, which is a function that accepts the encstep and also the hash +// of the previous encstep. Note that the hash is not part of the encstep. +// +// An animation queue consists of a list of steps. Each step has a hash +// and an encstep. An animation queue is serialized as follows: +// +// for all animation steps, starting with the most recent, do: +// write_uint64(hash) +// write_string(encstep) +// +// The encoded string produced by the loop above is called an "encqueue", +// because it encodes everything in the animation queue (except for the +// size limit, which is separate). +// +// Since the steps in an encqueue are stored most-recent first, if you +// want some information about the most recent animation entry, you +// don't need to decode the entire encqueue. You only need to +// decode the first step. +// /////////////////////////////////////////////////////////////////// #ifndef ANIMQUEUE_HPP @@ -40,7 +89,6 @@ #include "streambuffer.hpp" #include "debugcollector.hpp" #include "util.hpp" -#include "enginewrapper.hpp" #include #include @@ -190,14 +238,6 @@ struct AnimCoreState }; class AnimQueue : public eng::nevernew { -private: - struct Step { - Step() { hash=0; } - Step(const eng::string &e, uint64_t h) : encoding(e), hash(h) {} - eng::string encoding; - uint64_t hash; - }; - public: // Construct an empty animation queue. // clears the state to a valid state. @@ -250,10 +290,6 @@ public: eng::string steps_debug_string() const; eng::string full_debug_string() const; - // Get the final hash value. - // - uint64_t get_final_hash() const { return steps_.back().hash; } - // Get the final entry, xyz and plane only. // AnimCoreState get_final_core_state() const; @@ -266,17 +302,22 @@ public: // AnimState get_final_everything() const; - // Get the contents of the animation queue for export to the engine wrapper. + // Get a serialized representation of the animation queue. // - // Caution: this exports pointers into existing allocated strings. - // The pointers in this array are only valid until you modify the - // animation queue. + // Get the entire animation queue in a serialized format (encqueue). + // The string returned is a shared string. No string copy + // is made during this process. // - void get_for_engine_wrapper(std::vector *into) const; + util::SharedStdString get_encoded_queue() const { return encqueue_; } private: int size_limit_; - eng::deque steps_; + + // Note: this is stored as a std::string, not an eng::string, because the + // ownership ends up being shared between us and the graphics engine. We + // can't have the graphics engine affecting the behavior of the engine heap. + // + util::SharedStdString encqueue_; }; #endif // ANIMQUEUE_HPP diff --git a/luprex/cpp/core/drivenengine.cpp b/luprex/cpp/core/drivenengine.cpp index 75b9047c..d9fb5ad2 100644 --- a/luprex/cpp/core/drivenengine.cpp +++ b/luprex/cpp/core/drivenengine.cpp @@ -377,30 +377,21 @@ void DrivenEngine::drv_get_tangibles_near(uint64_t tanid, double rx, double ry, assert(hash1 == hash2); } -void DrivenEngine::drv_get_animation_queue_hashes(uint32_t count, const int64_t *ids, uint64_t *hashes) { - if (visible_world_ == 0) { +void DrivenEngine::drv_get_animation_queues(uint32_t count, const int64_t *ids, uint32_t *lengths, const char **strings) { + anim_queues_.resize(count); + + if (visible_world_ == nullptr) { + util::SharedStdString empty = std::make_shared(""); for (int i = 0; i < int(count); i++) { - hashes[i] = 0; + anim_queues_[i] = empty; } } else { - visible_world_->get_animation_queue_hashes(count, ids, hashes); + visible_world_->get_encoded_animation_queues(count, ids, anim_queues_); } -} - -void DrivenEngine::drv_get_animation_queue(uint64_t tanid, uint32_t *count, AnimEntry **entries) { - uint32_t hash1 = eng::memhash(); - anim_queue_result_.clear(); - if ((visible_world_ != 0) && (tanid != 0)) { - visible_world_->get_animation_queue(tanid, &anim_queue_result_); + for (int i = 0; i < int(count); i++) { + lengths[i] = anim_queues_[i]->size(); + strings[i] = anim_queues_[i]->c_str(); } - *count = anim_queue_result_.size(); - if (count > 0) { - *entries = &(anim_queue_result_[0]); - } else { - *entries = nullptr; - } - uint32_t hash2 = eng::memhash(); - assert(hash1 == hash2); } ////////////////////////////////////////////////////////////////////////////// @@ -532,13 +523,10 @@ static void drv_get_tangibles_near(EngineWrapper *w, uint64_t tanid, double rx, return w->engine->drv_get_tangibles_near(tanid, rx, ry, rz, count, ids); } -static void drv_get_animation_queue_hashes(EngineWrapper *w, uint32_t count, const int64_t *ids, uint64_t *hashes) { - return w->engine->drv_get_animation_queue_hashes(count, ids, hashes); +static void drv_get_animation_queues(EngineWrapper *w, uint32_t count, const int64_t *ids, uint32_t *lengths, const char **strings) { + return w->engine->drv_get_animation_queues(count, ids, lengths, strings); } -static void drv_get_animation_queue(EngineWrapper *w, int64_t tanid, uint32_t *count, EngineWrapper::AnimEntry **entries) { - return w->engine->drv_get_animation_queue(tanid, count, entries); -} ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -935,9 +923,8 @@ static void init_engine_wrapper_helper(EngineWrapper *w) { w->get_stop_driver = drv_get_stop_driver; w->get_actor_id = drv_get_actor_id; w->get_tangibles_near = drv_get_tangibles_near; - w->get_animation_queue_hashes = drv_get_animation_queue_hashes; - w->get_animation_queue = drv_get_animation_queue; - + w->get_animation_queues = drv_get_animation_queues; + w->play_initialize = play_initialize; w->play_clear_new_outgoing = play_clear_new_outgoing; w->play_sent_outgoing = play_sent_outgoing; diff --git a/luprex/cpp/core/drivenengine.hpp b/luprex/cpp/core/drivenengine.hpp index c47c3ddf..b8c8c265 100644 --- a/luprex/cpp/core/drivenengine.hpp +++ b/luprex/cpp/core/drivenengine.hpp @@ -125,8 +125,6 @@ using SharedChannel = std::shared_ptr; class DrivenEngine : public eng::opnew { public: - using AnimEntry = EngineWrapper::AnimEntry; - ////////////////////////////////////////////////////////////// // // Build the named engine @@ -284,8 +282,7 @@ public: bool drv_get_stop_driver() const; uint64_t drv_get_actor_id() const; void drv_get_tangibles_near(uint64_t tanid, double rx, double ry, double rz, uint32_t *count, int64_t **ids); - void drv_get_animation_queue_hashes(uint32_t count, const int64_t *ids, uint64_t *hashes); - void drv_get_animation_queue(uint64_t tanid, uint32_t *count, AnimEntry **entries); + void drv_get_animation_queues(uint32_t count, const int64_t *ids, uint32_t *lengths, const char **strings); void drv_initialize(uint32_t srcpklen, const char *srcpk, int argc, char **argv); void drv_clear_new_outgoing(); @@ -315,7 +312,7 @@ private: World *visible_world_; int64_t visible_actor_id_; util::IdVector scan_result_; - std::vector anim_queue_result_; + std::vector anim_queues_; bool rescan_lua_source_; double clock_; bool stop_driver_; diff --git a/luprex/cpp/core/enginewrapper.hpp b/luprex/cpp/core/enginewrapper.hpp index 6be138ef..e69a2cc9 100644 --- a/luprex/cpp/core/enginewrapper.hpp +++ b/luprex/cpp/core/enginewrapper.hpp @@ -31,22 +31,6 @@ struct EngineWrapper { PlayLogfile *wlog; ReplayLogfile *rlog; - ////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// - // - // STRUCTURES - // - // These structures are used to return complicated values. - // - ////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// - - struct AnimEntry { - uint64_t hash; - uint32_t size; - const char *data; - }; - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // @@ -133,25 +117,19 @@ struct EngineWrapper { // Do a scan to find tangibles near the specified player. // // Returns a count and a pointer to an array of tangible IDs. The returned - // pointer is valid until the next call into the engine. + // pointer is valid until the next call to get_tangibles_near. // void (*get_tangibles_near)(EngineWrapper *w, uint64_t tanid, double rx, double ry, double rz, uint32_t *count, int64_t **ids); - // Get the hash value of the final animation step for each tangible. + // Get the animation queues for the specified tangibles. // - // You must supply an array of tangible IDs. The animation queue hash for - // each such tangible will be looked up. You must supply a buffer to - // hold the returned hashes. + // You must supply an array of tangible IDs. This returns the serialized + // animation queues for all specified tangibles as strings. The serialized + // format is documented in header file animqueue.hpp. The returned pointers + // remain valid until the next call to get_animation_queues. // - void (*get_animation_queue_hashes)(EngineWrapper *w, uint32_t count, const int64_t *ids, uint64_t *hashes); + void (*get_animation_queues)(EngineWrapper *w, uint32_t count, const int64_t *ids, uint32_t *lengths, const char **strings); - // Get the entire contents of an animation queue. - // - // Returns a count and a pointer to an array of AnimEntry. The returned - // pointer is valid until the next call into the engine. - // - void (*get_animation_queue)(EngineWrapper *w, int64_t tanid, uint32_t *count, AnimEntry **buffer); - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // diff --git a/luprex/cpp/core/streambuffer.cpp b/luprex/cpp/core/streambuffer.cpp index 7e92471c..900418a0 100644 --- a/luprex/cpp/core/streambuffer.cpp +++ b/luprex/cpp/core/streambuffer.cpp @@ -169,6 +169,15 @@ void StreamBuffer::read_bytes_into(char *data, int64_t size) { read_cursor_ += size; } +std::string_view StreamBuffer::read_string_view_limit(uint64_t limit) { + uint64_t length = read_length(); + if (length > limit) throw StreamCorruption(); + check_available(length); + std::string_view result(read_cursor_, length); + read_cursor_ += length; + return result; +} + void StreamBuffer::write_xyz(const util::XYZ &xyz) { make_space(12); memcpy(write_cursor_, &xyz.x, 4); diff --git a/luprex/cpp/core/streambuffer.hpp b/luprex/cpp/core/streambuffer.hpp index 721bd402..3759c5c3 100644 --- a/luprex/cpp/core/streambuffer.hpp +++ b/luprex/cpp/core/streambuffer.hpp @@ -304,6 +304,11 @@ public: // void read_bytes_into(char *target, int64_t len); + // Read a string as a string_view. + // + std::string_view read_string_view_limit(uint64_t limit); + std::string_view read_string_view() { return read_string_view_limit(0x1000000); } + // Read and write larger types. // // Throws StreamEof if the specified number of bytes aren't present. @@ -385,7 +390,7 @@ public: // This is always false, because this module throws exceptions // when reading beyond EOF. bool read_beyond_eof() { return false; } - + private: // Start and end of the allocated block. char *buf_lo_; diff --git a/luprex/cpp/core/util.hpp b/luprex/cpp/core/util.hpp index e44376df..8cd193a9 100644 --- a/luprex/cpp/core/util.hpp +++ b/luprex/cpp/core/util.hpp @@ -223,6 +223,8 @@ using StringSet = eng::set; using LuaSourceVec = eng::vector; using LuaSourcePtr = std::unique_ptr; using HashValue = std::pair; +using SharedStdString = std::shared_ptr; +using SharedStdStringVec = std::vector; // Ascii uppercase and lowercase. eng::string ascii_tolower(std::string_view c); diff --git a/luprex/cpp/core/world-core.cpp b/luprex/cpp/core/world-core.cpp index c39bf6ad..1b3aaf7d 100644 --- a/luprex/cpp/core/world-core.cpp +++ b/luprex/cpp/core/world-core.cpp @@ -227,24 +227,19 @@ void World::get_near(int64_t player_id, float radius, bool exclude_nowhere, bool get_near(scan, into); } -void World::get_animation_queue_hashes(uint32_t count, const int64_t *ids, uint64_t *hashes) { +void World::get_encoded_animation_queues(uint32_t count, const int64_t *ids, util::SharedStdStringVec &into) { + util::SharedStdString empty = std::make_shared(""); + into.resize(count); for (int i = 0; i < int(count); i++) { Tangible *tan = tangible_get(ids[i]); - if (tan == 0) { - hashes[i] = 0; + if (tan == nullptr) { + into[i] = empty; } else { - hashes[i] = tan->anim_queue_.get_final_hash(); + into[i] = tan->anim_queue_.get_encoded_queue(); } } } -void World::get_animation_queue(int64_t tanid, std::vector *into) { - Tangible *tan = tangible_get(tanid); - if (tan != nullptr) { - tan->anim_queue_.get_for_engine_wrapper(into); - } -} - World::Redirects World::fetch_redirects() { World::Redirects result = std::move(redirects_); redirects_.clear(); diff --git a/luprex/cpp/core/world.hpp b/luprex/cpp/core/world.hpp index 4714cf17..84bba880 100644 --- a/luprex/cpp/core/world.hpp +++ b/luprex/cpp/core/world.hpp @@ -135,19 +135,11 @@ public: // void get_near(int64_t player_id, float radius, bool exclude_nowhere, bool omit_player, bool sorted, IdVector *into) const; - // get animation queue hashes. + // get encoded animation queues. // - // This is used by the graphics engine to check if any of the specified - // animation queues has changed in any way. + // This is used by the graphics engine to get the animation queues. // - void get_animation_queue_hashes(uint32_t count, const int64_t *ids, uint64_t *hashes); - - // Get the animation queue for the graphics engine. - // - // Gets the animation queue of a tangible in a form that can be - // passed to the graphics engine. - // - void get_animation_queue(int64_t tanid, std::vector *into); + void get_encoded_animation_queues(uint32_t count, const int64_t *ids, util::SharedStdStringVec &into); // Make a tangible. //