2021-01-12 14:14:38 -05:00
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// ANIMATION QUEUES
|
|
|
|
|
//
|
|
|
|
|
// An animation queue is a fifo queue of animation steps. New animations are
|
|
|
|
|
// pushed on the back, and old ones are popped from the front.
|
|
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
// An animation step is a set of key-value pairs, where each key is an
|
|
|
|
|
// identifier, and each value is either a number, a boolean, an XYZ coordinate,
|
|
|
|
|
// or a string. A key-value pair can be either persistent or nonpersistent.
|
|
|
|
|
// So a typical animation step might be:
|
2021-01-12 14:14:38 -05:00
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
// action=walk [nonpersistent]
|
|
|
|
|
// xyz=3,4,5 [persistent]
|
|
|
|
|
// facing=320 [persistent]
|
|
|
|
|
// plane=earth [persistent]
|
2021-01-12 14:14:38 -05:00
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
// Persistent values are retained from one animation step to the next,
|
|
|
|
|
// nonpersistent values exist for one animation step only.
|
2021-01-12 14:14:38 -05:00
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
// 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.
|
2021-07-18 17:48:39 -04:00
|
|
|
//
|
2023-07-26 17:40:20 -04:00
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// SERIALIZED STORAGE
|
|
|
|
|
//
|
|
|
|
|
// The entired animation queue is stored in a serialized format,
|
|
|
|
|
// as a shared string. This means that the animation queue can be
|
2023-08-16 15:40:30 -04:00
|
|
|
// passed to the graphics engine as a single string. This can be
|
|
|
|
|
// accomplished by the function EngineWrapper.get_animation_queues.
|
|
|
|
|
//
|
|
|
|
|
// The fact that the queue is stored in a serialized format means
|
|
|
|
|
// that when manipulating the animation queue, we have to decode it,
|
|
|
|
|
// manipulate it, and then reencode it.
|
|
|
|
|
//
|
|
|
|
|
// From an efficiency perspective, this means that manipulation is
|
|
|
|
|
// slower, but passing the strings to the graphics engine is faster
|
|
|
|
|
// and simpler. This is a good tradeoff: we manipulate animation
|
|
|
|
|
// queues rarely, compared to how often we pass them to the graphics
|
|
|
|
|
// engine.
|
|
|
|
|
//
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
2023-07-26 17:40:20 -04:00
|
|
|
//
|
2023-08-16 15:40:30 -04:00
|
|
|
// THE SERIALIZED REPRESENTATION
|
2023-07-26 17:40:20 -04:00
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
2023-09-28 19:31:48 -04:00
|
|
|
// A serialized animation queue consists of the following information:
|
2023-07-26 17:40:20 -04:00
|
|
|
//
|
2023-09-28 19:31:48 -04:00
|
|
|
// write_uint8(size_limit);
|
|
|
|
|
// write_uint8(actual_size);
|
|
|
|
|
// for all animation steps, starting with the most recent, do:
|
|
|
|
|
// write_uint64(hash)
|
|
|
|
|
// write_string(encstep)
|
2023-07-26 17:40:20 -04:00
|
|
|
//
|
|
|
|
|
// The encoded string produced by the loop above is called an "encqueue",
|
2023-09-28 19:31:48 -04:00
|
|
|
// because it encodes everything in the animation queue.
|
|
|
|
|
//
|
|
|
|
|
// Note that the 'serialize' routine for animation queues just returns
|
|
|
|
|
// the encqueue string, which is the whole thing.
|
2023-07-26 17:40:20 -04:00
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
2021-01-12 14:14:38 -05:00
|
|
|
///////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#ifndef ANIMQUEUE_HPP
|
|
|
|
|
#define ANIMQUEUE_HPP
|
|
|
|
|
|
2022-02-23 23:08:28 -05:00
|
|
|
#include "wrap-set.hpp"
|
|
|
|
|
#include "wrap-string.hpp"
|
|
|
|
|
#include "wrap-deque.hpp"
|
|
|
|
|
#include "wrap-unordered-map.hpp"
|
|
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
#include "luastack.hpp"
|
2021-03-13 13:05:00 -05:00
|
|
|
#include "streambuffer.hpp"
|
2021-11-21 13:35:39 -05:00
|
|
|
#include "debugcollector.hpp"
|
2021-01-12 14:14:38 -05:00
|
|
|
#include "util.hpp"
|
|
|
|
|
|
2022-02-24 02:17:41 -05:00
|
|
|
#include <cassert>
|
2022-02-25 19:57:23 -05:00
|
|
|
#include <ostream>
|
2021-01-12 14:14:38 -05:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
enum AnimValueType {
|
|
|
|
|
T_STRING,
|
|
|
|
|
T_NUMBER,
|
|
|
|
|
T_BOOLEAN,
|
|
|
|
|
T_XYZ,
|
|
|
|
|
T_UNINITIALIZED
|
|
|
|
|
};
|
2021-01-12 14:14:38 -05:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
struct AnimValue {
|
|
|
|
|
AnimValue() : persistent(false), type(T_UNINITIALIZED) {}
|
|
|
|
|
|
|
|
|
|
bool persistent;
|
|
|
|
|
AnimValueType type;
|
|
|
|
|
eng::string str;
|
|
|
|
|
util::DXYZ xyz;
|
|
|
|
|
|
|
|
|
|
// These set the type, str, and xyz fields in a consistent way.
|
|
|
|
|
void set_boolean(bool b);
|
|
|
|
|
void set_number(double n);
|
|
|
|
|
void set_xyz(const util::DXYZ &xyz);
|
|
|
|
|
void set_string(std::string_view s);
|
|
|
|
|
|
|
|
|
|
// The get the type, str, and xyz fields in a consistent way.
|
|
|
|
|
bool get_boolean() const;
|
|
|
|
|
double get_number() const;
|
|
|
|
|
const util::DXYZ &get_xyz() const;
|
|
|
|
|
std::string_view get_string() const;
|
|
|
|
|
|
|
|
|
|
// Copy the value from another animvalue. Don't copy the persistent flag.
|
|
|
|
|
void copy_value(const AnimValue &other);
|
|
|
|
|
};
|
2021-03-28 15:12:09 -04:00
|
|
|
|
|
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
class AnimState
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
using Map = eng::map<eng::string, AnimValue>;
|
|
|
|
|
Map map_;
|
2021-07-13 15:18:37 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Set the default value, internal
|
|
|
|
|
//
|
|
|
|
|
eng::string add_default(const eng::string &name, const AnimValue &v, const AnimState *other);
|
2021-07-13 15:18:37 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
public:
|
|
|
|
|
// Clear everything
|
|
|
|
|
//
|
|
|
|
|
void clear() { map_.clear(); }
|
2021-07-13 15:18:37 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Set the persistent flag on a single variable.
|
|
|
|
|
// If the variable isn't present, add it.
|
2021-07-13 15:18:37 -04:00
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
void set_persistent(const eng::string &name);
|
2021-07-13 15:18:37 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Return if it contains a value for the specified name.
|
2021-03-28 15:12:09 -04:00
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
bool contains(const eng::string &name) { return map_.find(name) != map_.end(); }
|
|
|
|
|
|
|
|
|
|
// Get the value of a specific variable.
|
|
|
|
|
// If the variable isn't present, return a default value.
|
2021-03-28 15:12:09 -04:00
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
bool get_boolean(const eng::string &name);
|
|
|
|
|
double get_number(const eng::string &name);
|
|
|
|
|
util::DXYZ get_xyz(const eng::string &name);
|
|
|
|
|
std::string_view get_string(const eng::string &name);
|
|
|
|
|
|
|
|
|
|
// Set a single variable to a value of a specified type.
|
|
|
|
|
// If the variable isn't present, add it.
|
2021-06-03 13:29:19 -04:00
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
void set_boolean(const eng::string &name, bool v);
|
|
|
|
|
void set_number(const eng::string &name, double v);
|
|
|
|
|
void set_xyz(const eng::string &name, const util::DXYZ &value);
|
|
|
|
|
void set_string(const eng::string &name, std::string_view value);
|
2021-03-28 15:12:09 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Print a debug string into the stringstream.
|
|
|
|
|
//
|
|
|
|
|
void print_debug_string(eng::ostringstream &oss);
|
2021-07-13 15:18:37 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Return the debug string.
|
|
|
|
|
eng::string debug_string();
|
2021-03-28 15:12:09 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Constructs an empty state.
|
|
|
|
|
//
|
|
|
|
|
AnimState() {}
|
2021-07-21 00:50:06 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Convert to an encoded string.
|
|
|
|
|
//
|
|
|
|
|
eng::string encode() const;
|
2021-07-21 00:50:06 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Decode an encoded string.
|
|
|
|
|
//
|
|
|
|
|
void decode(std::string_view enc);
|
2021-07-21 16:10:29 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Decode an encoded string, discarding non-persistent data.
|
|
|
|
|
//
|
|
|
|
|
void decode_persistent(std::string_view enc);
|
2021-07-21 16:10:29 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Add default values for all builtin persistent variables.
|
|
|
|
|
//
|
|
|
|
|
// For each builtin default (plane, xyz, facing, bp, model)
|
|
|
|
|
//
|
|
|
|
|
// - Will generate an error if a value is already present,
|
|
|
|
|
// but the present value is of the wrong type.
|
|
|
|
|
//
|
|
|
|
|
// - If 'other' is not nullptr, then we look in 'other' for a default
|
|
|
|
|
// value. If a valid value of the correct type is present, it is
|
|
|
|
|
// used as the default value.
|
|
|
|
|
//
|
|
|
|
|
// - If no default value can be found in 'other', then a hardwired
|
|
|
|
|
// default value is provided.
|
|
|
|
|
//
|
|
|
|
|
eng::string add_defaults(const AnimState *other);
|
|
|
|
|
|
|
|
|
|
// Apply a configuration from a lua table.
|
|
|
|
|
//
|
|
|
|
|
// If a key already exists in the state, then type type from the table
|
|
|
|
|
// must match the type from the existing state.
|
|
|
|
|
//
|
|
|
|
|
eng::string apply_lua(LuaCoreStack &LS0, LuaSlot tab, bool setpersist);
|
|
|
|
|
|
|
|
|
|
// Convert an animstate to a lua table.
|
|
|
|
|
//
|
|
|
|
|
// You can either convert the persistent key-value pairs, or the
|
|
|
|
|
// nonpersistent. So you'll need two lua tables to store both.
|
|
|
|
|
//
|
|
|
|
|
void to_lua(LuaCoreStack &LS, LuaSlot tab, bool persistent);
|
|
|
|
|
|
|
|
|
|
// Parse a string, for unit testing.
|
|
|
|
|
//
|
|
|
|
|
void parse(std::string_view s);
|
|
|
|
|
void clear_and_parse(std::string_view s);
|
2021-07-21 16:10:29 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Constructor from an encoded string.
|
|
|
|
|
//
|
|
|
|
|
AnimState(std::string_view s) { decode(s); }
|
2021-01-12 14:14:38 -05:00
|
|
|
};
|
|
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
struct AnimCoreState
|
|
|
|
|
{
|
|
|
|
|
util::DXYZ xyz;
|
|
|
|
|
eng::string plane;
|
|
|
|
|
|
|
|
|
|
void decode(std::string_view enc);
|
|
|
|
|
};
|
2021-07-21 00:50:06 -04:00
|
|
|
|
2022-03-02 14:52:51 -05:00
|
|
|
class AnimQueue : public eng::nevernew {
|
2021-07-21 16:10:29 -04:00
|
|
|
public:
|
2023-07-24 17:19:25 -04:00
|
|
|
// Construct an empty animation queue.
|
|
|
|
|
// clears the state to a valid state.
|
|
|
|
|
//
|
|
|
|
|
AnimQueue();
|
2021-01-12 14:14:38 -05:00
|
|
|
|
2023-09-28 19:31:48 -04:00
|
|
|
// Clear the steps to an initial state.
|
2023-07-24 17:19:25 -04:00
|
|
|
//
|
|
|
|
|
void clear();
|
|
|
|
|
void clear(const AnimState &initial);
|
2023-09-28 19:31:48 -04:00
|
|
|
|
|
|
|
|
// Change the size limit.
|
2023-07-24 17:19:25 -04:00
|
|
|
//
|
2023-09-28 19:31:48 -04:00
|
|
|
void set_limit(int limit);
|
2023-07-24 17:19:25 -04:00
|
|
|
|
|
|
|
|
// Add an animation step.
|
|
|
|
|
//
|
|
|
|
|
// Note: add does not automatically compose the step with the previous
|
|
|
|
|
// step, you have to do that yourself.
|
2021-08-03 11:25:12 -04:00
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
void add(const AnimState &state);
|
|
|
|
|
|
2023-10-03 18:17:24 -04:00
|
|
|
// Replace the most recent animation step.
|
|
|
|
|
//
|
|
|
|
|
// Note: replace does not automatically compose the step with the previous
|
|
|
|
|
// step, you have to do that yourself.
|
|
|
|
|
//
|
|
|
|
|
void replace(const AnimState &state);
|
|
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Serialize or deserialize to a StreamBuffer
|
2021-08-03 11:25:12 -04:00
|
|
|
//
|
2021-07-26 13:12:44 -04:00
|
|
|
void serialize(StreamBuffer *sb) const;
|
2021-03-13 13:05:00 -05:00
|
|
|
void deserialize(StreamBuffer *sb);
|
|
|
|
|
|
2021-07-13 15:18:37 -04:00
|
|
|
// Difference transmission
|
2023-07-24 17:19:25 -04:00
|
|
|
//
|
2021-11-21 13:35:39 -05:00
|
|
|
bool diff(const AnimQueue &auth, StreamBuffer *sb) const;
|
|
|
|
|
void patch(StreamBuffer *sb, DebugCollector *dbc);
|
2021-01-17 16:23:10 -05:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Check for exactly equal. This does the full check of all
|
|
|
|
|
// fields, it is used exclusively for debugging.
|
2021-08-03 11:25:12 -04:00
|
|
|
//
|
2023-07-24 17:19:25 -04:00
|
|
|
bool exactly_equal(const AnimQueue &aq) const;
|
2021-08-03 11:25:12 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Check for exactly equal (fast). Compares the size, limit,
|
|
|
|
|
// and hash of the last entry. If these are equal, then the whole
|
|
|
|
|
// thing should be equal.
|
|
|
|
|
//
|
|
|
|
|
bool exactly_equal_fast(const AnimQueue &aq) const;
|
2021-07-21 00:50:06 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Debug strings.
|
|
|
|
|
//
|
2023-08-16 15:40:30 -04:00
|
|
|
void print_debug_string(eng::ostringstream &oss, bool full) const;
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::string steps_debug_string() const;
|
|
|
|
|
eng::string full_debug_string() const;
|
2021-07-21 16:10:29 -04:00
|
|
|
|
2023-07-24 17:19:25 -04:00
|
|
|
// Get the final entry, xyz and plane only.
|
|
|
|
|
//
|
|
|
|
|
AnimCoreState get_final_core_state() const;
|
|
|
|
|
|
|
|
|
|
// Get the final entry, all persistent variables.
|
|
|
|
|
//
|
|
|
|
|
AnimState get_final_persistent() const;
|
|
|
|
|
|
|
|
|
|
// Get the final entry, everything persistent and non-persistent.
|
|
|
|
|
//
|
|
|
|
|
AnimState get_final_everything() const;
|
|
|
|
|
|
2023-07-26 17:40:20 -04:00
|
|
|
// Get a serialized representation of the animation queue.
|
2023-07-24 17:21:44 -04:00
|
|
|
//
|
2023-07-26 17:40:20 -04:00
|
|
|
// 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.
|
2023-07-24 17:21:44 -04:00
|
|
|
//
|
2023-07-26 17:40:20 -04:00
|
|
|
util::SharedStdString get_encoded_queue() const { return encqueue_; }
|
2023-07-24 17:21:44 -04:00
|
|
|
|
2021-07-21 16:10:29 -04:00
|
|
|
private:
|
2023-10-03 18:17:24 -04:00
|
|
|
struct QueueRange {
|
|
|
|
|
int size;
|
|
|
|
|
std::string_view entries;
|
|
|
|
|
QueueRange(int sz, std::string_view ent) : size(sz), entries(ent) {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Get a range of entries from the queue.
|
2023-09-28 19:31:48 -04:00
|
|
|
//
|
2023-10-03 18:17:24 -04:00
|
|
|
// You must specify a range (lo-hi) of steps. In this numbering, 0 is the
|
|
|
|
|
// most recent entry in the queue. The indices lo and hi are automatically
|
|
|
|
|
// clamped to the valid range (0 to actual_size). If lo >= hi, then an
|
|
|
|
|
// empty range is returned.
|
2023-09-28 19:31:48 -04:00
|
|
|
//
|
2023-10-03 18:17:24 -04:00
|
|
|
QueueRange get_range(int lo, int hi);
|
2023-07-26 17:40:20 -04:00
|
|
|
|
2023-10-03 18:17:24 -04:00
|
|
|
// Hash a new step given the range of steps that precede it.
|
|
|
|
|
//
|
|
|
|
|
static uint64_t hash_encstep(const QueueRange &prev, std::string_view step);
|
|
|
|
|
|
|
|
|
|
// Update the animation queue.
|
|
|
|
|
//
|
|
|
|
|
// The range (keeplo to keephi) specifies which old steps should be
|
|
|
|
|
// retained. The numbers keephi and keeplo are automatically clamped
|
|
|
|
|
// so that they lie inside the actual size of the queue.
|
|
|
|
|
//
|
|
|
|
|
// If add is true, then an additional step is added to the queue.
|
|
|
|
|
// The hash of the new step is calculated automatically.
|
|
|
|
|
//
|
|
|
|
|
// There is no enforcement that you respected the size limit that you
|
|
|
|
|
// specified. For example, you could say "keep 0-5, and add 1." That
|
|
|
|
|
// would make 6 entries in the queue. It is up to the caller to respect
|
|
|
|
|
// the size limit. The value passed in is just for reporting.
|
|
|
|
|
//
|
|
|
|
|
void update_encqueue(int limit, bool add, std::string_view add_enc, int keeplo, int keephi);
|
|
|
|
|
|
2023-09-28 19:31:48 -04:00
|
|
|
// Read values from the header of the encqueue.
|
|
|
|
|
//
|
|
|
|
|
int get_size_limit() const;
|
|
|
|
|
int get_actual_size() const;
|
|
|
|
|
uint64_t get_final_hash() const;
|
|
|
|
|
std::string_view get_final_encstep() const;
|
|
|
|
|
|
|
|
|
|
private:
|
2023-07-26 17:40:20 -04:00
|
|
|
// 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_;
|
2021-01-12 14:14:38 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif // ANIMQUEUE_HPP
|
|
|
|
|
|