diff --git a/luprex/cpp/core/animqueue.cpp b/luprex/cpp/core/animqueue.cpp index 10c7bb42..8dcfb685 100644 --- a/luprex/cpp/core/animqueue.cpp +++ b/luprex/cpp/core/animqueue.cpp @@ -9,95 +9,12 @@ #include #include -static const char *vtname(AnimValueType vt) { - switch (vt) { - case T_UNINITIALIZED: return "uninitialized"; - case T_BOOLEAN: return "boolean"; - case T_NUMBER: return "number"; - case T_STRING: return "string"; - case T_XYZ: return "xyz"; - default: return "unknown"; - } -} static uint64_t hash_encstep(uint64_t prev, std::string_view s) { return util::hash_string(util::HashValue(123, prev), s).first; } -void AnimValue::set_boolean(bool b) { - type = T_BOOLEAN; - str.clear(); - xyz = (b ? 1.0:0.0); -} - -void AnimValue::set_number(double n) { - type = T_NUMBER; - str.clear(); - xyz = util::DXYZ(n, 0, 0); -} - -void AnimValue::set_xyz(const util::DXYZ &v) { - type = T_XYZ; - str.clear(); - xyz = v; -} - -void AnimValue::set_string(std::string_view sv) { - type = T_STRING; - str = sv; - xyz = 0.0; -} - -bool AnimValue::get_boolean() const { - if (type != T_BOOLEAN) return false; - return (xyz.x == 1.0); -} - -double AnimValue::get_number() const { - if (type != T_NUMBER) return 0.0; - return xyz.x; -} - -const util::DXYZ &AnimValue::get_xyz() const { - static util::DXYZ zero; - if (type != T_XYZ) return zero; - return xyz; -} - -std::string_view AnimValue::get_string() const { - if (type != T_STRING) return std::string_view(""); - return std::string_view(str); -} - -void AnimValue::copy_value(const AnimValue &other) { - type = other.type; - str = other.str; - xyz = other.xyz; -} - -static void encode_value(StreamBuffer *sb, const AnimValue *v) { - sb->write_uint8(uint8_t(v->type)); - switch(v->type) { - case T_NUMBER: sb->write_double(v->xyz.x); break; - case T_BOOLEAN: sb->write_bool(v->xyz.x == 1.0); break; - case T_XYZ: sb->write_dxyz(v->xyz); break; - case T_STRING: sb->write_string(v->str); break; - default: assert(false); - } -} - -static void decode_value(StreamBuffer *sb, AnimValue *v) { - AnimValueType type = AnimValueType(sb->read_uint8()); - switch (type) { - case T_NUMBER: v->set_number(sb->read_double()); break; - case T_BOOLEAN: v->set_boolean(sb->read_bool()); break; - case T_XYZ: v->set_xyz(sb->read_dxyz()); break; - case T_STRING: v->set_string(sb->read_string()); break; - default: assert(false); - } -} - // Parse a value. This is meant for unit testing only. The // parser isn't powerful enough to express all possible values. static void parse_value(std::string_view vstr, AnimValue *v) { @@ -122,7 +39,7 @@ static void parse_value(std::string_view vstr, AnimValue *v) { double x = std::atof(parts[0].c_str()); double y = std::atof(parts[1].c_str()); double z = std::atof(parts[2].c_str()); - v->set_xyz(util::DXYZ(x,y,z)); + v->set_dxyz(util::DXYZ(x,y,z)); return; } // If it doesn't parse as any of the above, it's a string. @@ -149,7 +66,7 @@ static AnimValue parse_anim_value(LuaCoreStack &LS, LuaSlot val, LuaSlot tmp) { LS.rawget(tmp, val, 3); if (!LS.isnumber(tmp)) return result; xyz.z = LS.cknumber(tmp); - result.set_xyz(xyz); + result.set_dxyz(xyz); } return result; } @@ -158,32 +75,6 @@ void AnimState::set_persistent(const eng::string &name) { map_[name].persistent = true; } -bool AnimState::get_boolean(const eng::string &name) { - auto iter = map_.find(name); - if (iter == map_.end()) return false; - return iter->second.get_boolean(); -} - -double AnimState::get_number(const eng::string &name) { - auto iter = map_.find(name); - if (iter == map_.end()) return 0.0; - return iter->second.get_number(); -} - -util::DXYZ AnimState::get_xyz(const eng::string &name) { - static util::DXYZ zero; - auto iter = map_.find(name); - if (iter == map_.end()) return zero; - return iter->second.get_xyz(); -} - -std::string_view AnimState::get_string(const eng::string &name) { - auto iter = map_.find(name); - if (iter == map_.end()) return std::string_view(""); - return iter->second.get_string(); -} - - void AnimState::set_boolean(const eng::string &name, bool v) { AnimValue &value = map_[name]; value.set_boolean(v); @@ -194,9 +85,9 @@ void AnimState::set_number(const eng::string &name, double v) { value.set_number(v); } -void AnimState::set_xyz(const eng::string &name, const util::DXYZ &v) { +void AnimState::set_dxyz(const eng::string &name, const util::DXYZ &v) { AnimValue &value = map_[name]; - value.set_xyz(v); + value.set_dxyz(v); } void AnimState::set_string(const eng::string &name, std::string_view v) { @@ -220,10 +111,10 @@ void AnimState::print_debug_string(eng::ostringstream &oss) { oss << ":"; } switch (value.type) { - case T_NUMBER: oss << value.xyz.x; break; - case T_BOOLEAN: oss << ((value.xyz.x == 1.0) ? "true":"false"); break; - case T_XYZ: oss << value.xyz; break; - case T_STRING: oss << value.str; break; + case SimpleDynamicTag::NUMBER: oss << value.x; break; + case SimpleDynamicTag::BOOLEAN: oss << ((value.x == 1.0) ? "true":"false"); break; + case SimpleDynamicTag::VECTOR: oss << value.x << "," << value.y << "," << value.z; break; + case SimpleDynamicTag::STRING: oss << value.s; break; default: assert(false); } first = false; @@ -243,7 +134,7 @@ eng::string AnimState::encode() const { const AnimValue &value = pair.second; sb.write_string(name); sb.write_bool(value.persistent); - encode_value(&sb, &value); + sb.write_simple_dynamic(value); } return sb.read_entire_contents(); } @@ -253,26 +144,25 @@ void AnimState::decode(std::string_view s) { StreamBuffer sb(s); while (!sb.empty()) { eng::string name = sb.read_string(); - bool persistent = sb.read_bool(); AnimValue &value = map_[name]; - value.persistent = persistent; - decode_value(&sb, &value); + value.persistent = sb.read_bool(); + sb.read_simple_dynamic(&value); } } void AnimState::decode_persistent(std::string_view s) { map_.clear(); StreamBuffer sb(s); - AnimValue dummy; + SimpleDynamic dummy; while (!sb.empty()) { eng::string name = sb.read_string(); bool persistent = sb.read_bool(); if (persistent) { AnimValue &value = map_[name]; value.persistent = persistent; - decode_value(&sb, &value); + sb.read_simple_dynamic(&value); } else { - decode_value(&sb, &dummy); + sb.read_simple_dynamic(&dummy); } } } @@ -280,7 +170,7 @@ void AnimState::decode_persistent(std::string_view s) { eng::string AnimState::add_default(const eng::string &name, const AnimValue &def, const AnimState *other) { AnimValue &value = map_[name]; value.persistent = true; - if (value.type == T_UNINITIALIZED) { + if (value.type == SimpleDynamicTag::UNINITIALIZED) { if (other != nullptr) { auto otheriter = other->map_.find(name); if (otheriter != other->map_.end()) { @@ -294,7 +184,7 @@ eng::string AnimState::add_default(const eng::string &name, const AnimValue &def return ""; } if (value.type != def.type) { - return util::ss("Animation key ", name, " must be a ", vtname(def.type)); + return util::ss("Animation key ", name, " must be a ", def.type_name()); } return ""; } @@ -303,7 +193,7 @@ eng::string AnimState::add_defaults(const AnimState *other) { eng::string err; AnimValue defval; - defval.set_xyz(util::DXYZ(0,0,0)); + defval.set_dxyz(util::DXYZ(0,0,0)); err = add_default("xyz", defval, other); if (!err.empty()) return err; @@ -341,13 +231,13 @@ eng::string AnimState::apply_lua(LuaCoreStack &LS0, LuaSlot tab, bool setpersist return "in animation key-value pairs, key must be a string."; } AnimValue parsedvalue = parse_anim_value(LS, val, tmp); - if (parsedvalue.type == T_UNINITIALIZED) { + if (parsedvalue.type == SimpleDynamicTag::UNINITIALIZED) { return "in animation key-value pairs, val must be number, string, boolean, or xyz"; } eng::string name = LS.ckstring(key); AnimValue &mapentry = map_[name]; - if ((mapentry.type != T_UNINITIALIZED) && (mapentry.type != parsedvalue.type)) { - return util::ss("animation '", name, "' must be a ", vtname(mapentry.type)); + if ((mapentry.type != SimpleDynamicTag::UNINITIALIZED) && (mapentry.type != parsedvalue.type)) { + return util::ss("animation '", name, "' must be a ", mapentry.type_name()); } mapentry.copy_value(parsedvalue); if (setpersist) mapentry.persistent = true; @@ -363,17 +253,17 @@ void AnimState::to_lua(LuaCoreStack &LS0, LuaSlot tab, bool persistent) { if (pair.second.persistent != persistent) continue; LS.set(name, pair.first); const AnimValue &value = pair.second; - if (value.type == T_BOOLEAN) { - LS.set(val, value.get_boolean()); - } else if (value.type == T_NUMBER) { - LS.set(val, value.get_number()); - } else if (value.type == T_STRING) { - LS.set(val, value.get_string()); - } else if (value.type == T_XYZ) { + if (value.type == SimpleDynamicTag::BOOLEAN) { + LS.set(val, (value.x == 1.0)); + } else if (value.type == SimpleDynamicTag::NUMBER) { + LS.set(val, value.x); + } else if (value.type == SimpleDynamicTag::STRING) { + LS.set(val, std::string_view(value.s)); + } else if (value.type == SimpleDynamicTag::VECTOR) { LS.newtable(val); - LS.rawset(val, 1, value.get_xyz().x); - LS.rawset(val, 2, value.get_xyz().y); - LS.rawset(val, 3, value.get_xyz().z); + LS.rawset(val, 1, value.x); + LS.rawset(val, 2, value.y); + LS.rawset(val, 3, value.z); } LS.rawset(tab, name, val); } @@ -407,14 +297,14 @@ void AnimCoreState::decode(std::string_view s) { plane.clear(); xyz = 0.0; StreamBuffer sb(s); - AnimValue value; + SimpleDynamic value; while (!sb.empty()) { eng::string name = sb.read_string(); bool persistent = sb.read_bool(); - decode_value(&sb, &value); + sb.read_simple_dynamic(&value); if (persistent) { - if ((name == "xyz") && (value.type == T_XYZ)) xyz = value.xyz; - if ((name == "plane") && (value.type == T_STRING)) plane = value.str; + if ((name == "xyz") && (value.type == SimpleDynamicTag::VECTOR)) xyz = util::DXYZ(value.x, value.y, value.z); + if ((name == "plane") && (value.type == SimpleDynamicTag::STRING)) plane = value.s; } } } @@ -642,28 +532,28 @@ LuaDefine(unittests_animqueue, "", "some unit tests") { // Test AnimState simple setters. astate.set_string("color", "blue"); - astate.set_xyz("xyz", util::DXYZ(1,2,3)); + astate.set_dxyz("xyz", util::DXYZ(1,2,3)); astate.set_number("half", 0.5); astate.set_boolean("nice", true); LuaAssertStrEq(L, astate.debug_string(), "color:blue half:0.5 nice:true xyz:1,2,3"); - // Test AnimState simple getters. - LuaAssert(L, astate.get_string("color") == "blue"); - LuaAssert(L, astate.get_xyz("xyz") == util::DXYZ(1,2,3)); - LuaAssert(L, astate.get_number("half") == 0.5); - LuaAssert(L, astate.get_boolean("nice") == true); + // // Test AnimState simple getters. + // LuaAssert(L, astate.get_string("color") == "blue"); + // LuaAssert(L, astate.get_xyz("xyz") == util::DXYZ(1,2,3)); + // LuaAssert(L, astate.get_number("half") == 0.5); + // LuaAssert(L, astate.get_boolean("nice") == true); - // Test AnimState simple getters on nonexistent data. - LuaAssert(L, astate.get_string("q") == ""); - LuaAssert(L, astate.get_xyz("q") == util::DXYZ(0,0,0)); - LuaAssert(L, astate.get_number("q") == 0.0); - LuaAssert(L, astate.get_boolean("q") == false); + // // Test AnimState simple getters on nonexistent data. + // LuaAssert(L, astate.get_string("q") == ""); + // LuaAssert(L, astate.get_xyz("q") == util::DXYZ(0,0,0)); + // LuaAssert(L, astate.get_number("q") == 0.0); + // LuaAssert(L, astate.get_boolean("q") == false); - // Test AnimState simple getters on wrong-type data. - LuaAssert(L, astate.get_string("half") == ""); - LuaAssert(L, astate.get_xyz("half") == util::DXYZ(0,0,0)); - LuaAssert(L, astate.get_number("color") == 0.0); - LuaAssert(L, astate.get_boolean("color") == false); + // // Test AnimState simple getters on wrong-type data. + // LuaAssert(L, astate.get_string("half") == ""); + // LuaAssert(L, astate.get_xyz("half") == util::DXYZ(0,0,0)); + // LuaAssert(L, astate.get_number("color") == 0.0); + // LuaAssert(L, astate.get_boolean("color") == false); // Test AnimState persistence manipulation. astate.set_persistent("color"); diff --git a/luprex/cpp/core/animqueue.hpp b/luprex/cpp/core/animqueue.hpp index 14434337..9022cd8f 100644 --- a/luprex/cpp/core/animqueue.hpp +++ b/luprex/cpp/core/animqueue.hpp @@ -93,6 +93,7 @@ #include "wrap-deque.hpp" #include "wrap-unordered-map.hpp" +#include "base-writer.hpp" #include "luastack.hpp" #include "streambuffer.hpp" #include "debugcollector.hpp" @@ -101,36 +102,15 @@ #include #include -enum AnimValueType { - T_STRING, - T_NUMBER, - T_BOOLEAN, - T_XYZ, - T_UNINITIALIZED -}; - -struct AnimValue { - AnimValue() : persistent(false), type(T_UNINITIALIZED) {} - +struct AnimValue : public SimpleDynamic { 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); + AnimValue() { persistent = false; } - // 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); + void set_dxyz(const util::DXYZ &xyz) { + type = SimpleDynamicTag::VECTOR; + s.clear(); x = xyz.x; y = xyz.y; z = xyz.z; + } }; @@ -158,20 +138,12 @@ public: // 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. - // - 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. // 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_dxyz(const eng::string &name, const util::DXYZ &value); void set_string(const eng::string &name, std::string_view value); // Print a debug string into the stringstream. diff --git a/luprex/cpp/core/world-testing.cpp b/luprex/cpp/core/world-testing.cpp index 93af7e2b..e9a5dc16 100644 --- a/luprex/cpp/core/world-testing.cpp +++ b/luprex/cpp/core/world-testing.cpp @@ -18,7 +18,7 @@ void World::tangible_clear_plane_and_xyz(int64_t id, const eng::string &plane, c assert(t != nullptr); AnimState state; state.set_string("plane", plane); - state.set_xyz("xyz", xyz); + state.set_dxyz("xyz", xyz); state.set_persistent("plane"); state.set_persistent("xyz"); t->anim_queue_.clear(state); @@ -30,7 +30,7 @@ void World::tangible_walkto(int64_t id, float x, float y) { assert(t != nullptr); AnimState state = t->anim_queue_.get_final_persistent(); state.set_string("action", "walkto"); - state.set_xyz("xyz", util::DXYZ(x,y,0.0)); + state.set_dxyz("xyz", util::DXYZ(x,y,0.0)); t->anim_queue_.add(state); } diff --git a/luprex/ext/base-writer.hpp b/luprex/ext/base-writer.hpp index 0ef75760..5722c79a 100644 --- a/luprex/ext/base-writer.hpp +++ b/luprex/ext/base-writer.hpp @@ -1,16 +1,90 @@ +#pragma once + ///////////////////////////////////////////////////////////////// // // IMPORTANT: This is a header-only library that is included // by the graphics engine as well. It cannot contain references // to anything else in the engine. // - +///////////////////////////////////////////////////////////////// #include #include #include +#include #include +/////////////////////////////////////////////////////////////// +// +// SimpleDynamic +// +// A struct that holds a dynamically typed value. +// This can hold a string, number, vector, or boolean. +// +// The type is stored in the 'type' field. +// +// If it's a STRING, the value is in the field s +// If it's a NUMBER, the value is in the field x +// If it's a BOOLEAN, it's true if (x==1.0) +// If it's a VECTOR, the value is in x,y,z +// +/////////////////////////////////////////////////////////////// + +enum class SimpleDynamicTag { + UNINITIALIZED, + STRING, + NUMBER, + BOOLEAN, + VECTOR, +}; + +struct SimpleDynamic { + SimpleDynamicTag type; + double x, y, z; + std::string s; + + SimpleDynamic() { + type = SimpleDynamicTag::UNINITIALIZED; + x=y=z=0; + } + + static const char *type_name_of(SimpleDynamicTag t) { + switch (t) { + case SimpleDynamicTag::UNINITIALIZED: return "uninitialized"; + case SimpleDynamicTag::BOOLEAN: return "boolean"; + case SimpleDynamicTag::NUMBER: return "number"; + case SimpleDynamicTag::STRING: return "string"; + case SimpleDynamicTag::VECTOR: return "vector"; + default: return "unknown"; + } + } + + const char *type_name() const { + return type_name_of(type); + } + + void set_string(std::string_view is) { + type=SimpleDynamicTag::STRING; s=is; x=0; y=0; z=0; + } + + void set_number(double n) { + type = SimpleDynamicTag::NUMBER; s.clear(); x=n; y=0; z=0; + } + + void set_boolean(bool b) { + type = SimpleDynamicTag::BOOLEAN; s.clear(); x=(b?1:0); y=0; z=0; + } + + void set_vector(double ix, double iy, double iz) { + type = SimpleDynamicTag::VECTOR; s.clear(); x=ix; y=iy; z=iz; + } + + void copy_value(const SimpleDynamic &other) { + type = other.type; + s=other.s; x=other.x; y=other.y; z=other.z; + } +}; + /////////////////////////////////////////////////////////////// // // BaseWriter @@ -30,6 +104,7 @@ // void write_double(double data) // void write_length(size_t data) // void write_string(std::string_view data) +// void write_simple_dynamic(const SimpleDynamic &sd); // // You should derive from BaseWriter using the CRTP pattern: // @@ -86,8 +161,20 @@ public: write_length(s.size()); static_cast(this)->write_bytes(s.data(), s.size()); } + + void write_simple_dynamic(const SimpleDynamic &sd) { + write_uint8(uint8_t(sd.type)); + switch(sd.type) { + case SimpleDynamicTag::NUMBER: write_double(sd.x); break; + case SimpleDynamicTag::BOOLEAN: write_bool(sd.x == 1.0); break; + case SimpleDynamicTag::VECTOR: write_double(sd.x); write_double(sd.y); write_double(sd.z); break; + case SimpleDynamicTag::STRING: write_string(sd.s); break; + default: assert(false); + } + } }; + /////////////////////////////////////////////////////////////// // // BaseReader @@ -109,6 +196,7 @@ public: // size_t read_length(); // String read_string_limit(uint64_t size); // String read_string(); +// SimpleDynamic read_simple_dynamic(); // // You should derive from BaseReader using the CRTP pattern: // @@ -184,5 +272,22 @@ public: } auto read_string() { return read_string_limit(0x1000000); } // 16MB limit default + + void read_simple_dynamic(SimpleDynamic *result) { + SimpleDynamicTag type = SimpleDynamicTag(read_uint8()); + switch (type) { + case SimpleDynamicTag::NUMBER: result->set_number(read_double()); break; + case SimpleDynamicTag::BOOLEAN: result->set_boolean(read_bool()); break; + case SimpleDynamicTag::VECTOR: { + double x=read_double(); + double y=read_double(); + double z=read_double(); + result->set_vector(x,y,z); + break; + } + case SimpleDynamicTag::STRING: result->set_string(read_string()); break; + default: assert(false); + } + } };