From a3530be6957840fb439355dffaf773f2cb49d500 Mon Sep 17 00:00:00 2001 From: Alejandro Canela Date: Wed, 13 May 2026 16:55:15 +0200 Subject: [PATCH 1/4] feat(common): add StructMapping + GenericNativeLoader for native C++ ingest --- docs/SAMPLES.md | 2 +- samples/advance_write.cpp | 24 +- samples/arena_mappings.h | 209 ++++++++-------- .../reader/arena/arena_from_fbs_ds.vtx | Bin 1783337 -> 1783327 bytes .../reader/arena/arena_from_json_ds.vtx | Bin 1783336 -> 1783327 bytes .../reader/arena/arena_from_proto_ds.vtx | Bin 1887291 -> 1887298 bytes .../common/adapters/native/struct_mapping.h | 98 ++++++++ .../common/readers/frame_reader/loader_base.h | 234 ++++++++++++++++++ .../readers/frame_reader/native_loader.h | 173 +++++++++++++ 9 files changed, 624 insertions(+), 116 deletions(-) create mode 100644 sdk/include/vtx/common/adapters/native/struct_mapping.h create mode 100644 sdk/include/vtx/common/readers/frame_reader/loader_base.h create mode 100644 sdk/include/vtx/common/readers/frame_reader/native_loader.h diff --git a/docs/SAMPLES.md b/docs/SAMPLES.md index b4fe84e..55e7d8a 100644 --- a/docs/SAMPLES.md +++ b/docs/SAMPLES.md @@ -12,7 +12,7 @@ samples/ generate_replay.cpp vtx_sample_generate arena simulator -> 3 data sources advance_write.cpp vtx_sample_advance_write 3 data sources -> 3 .vtx replays (with post-processor) - arena_mappings.h JSON data model + JsonMapping + ArenaToVtx::MapFrame() + arena_mappings.h JSON data model + JsonMapping + StructMapping + StructFrameBinding arena_generated.h autogenerated schema constants + typed Views + Mutators + ForEachX helpers schemas/ arena_data.proto Protobuf game schema (namespace arena_pb) diff --git a/samples/advance_write.cpp b/samples/advance_write.cpp index 1080a90..70f6139 100644 --- a/samples/advance_write.cpp +++ b/samples/advance_write.cpp @@ -18,8 +18,11 @@ // Mapping strategies demonstrated, one per format: // // JSON -> VTX::JsonMapping specializations in arena_mappings.h are -// walked by VTX::UniversalDeserializer to build ArenaReplayJson, -// then ArenaToVtx::MapFrame() produces each VTX::Frame. +// walked by VTX::UniversalDeserializer to build ArenaReplayJson. +// Then VTX::StructMapping + VTX::StructFrameBinding +// drive VTX::GenericNativeLoader::LoadFrame() to produce each +// VTX::Frame. Field addresses come from PropertyAddressCache, +// same as the FBS path below. // // Proto -> VTX::ProtoBinding specializations (below) are dispatched by // VTX::GenericProtobufLoader::LoadFrame(). Field-name lookups go @@ -43,6 +46,7 @@ #include "vtx/common/adapters/json/json_adapter.h" #include "vtx/common/vtx_types_helpers.h" #include "vtx/common/readers/frame_reader/flatbuffer_loader.h" +#include "vtx/common/readers/frame_reader/native_loader.h" #include "vtx/common/readers/frame_reader/protobuff_loader.h" #include "vtx/common/readers/frame_reader/universal_deserializer.h" #include "vtx/common/readers/schema_reader/schema_registry.h" @@ -276,13 +280,15 @@ namespace VTX { // Data source 1 -- JSON // =================================================================== // Parses the whole JSON blob on Initialize(), then cursors through the -// deserialized ArenaReplayJson frame-by-frame. The manual MapFrame() -// bridge from arena_mappings.h handles the arena-types -> VTX conversion. +// deserialized ArenaReplayJson frame-by-frame. GenericNativeLoader walks +// StructMapping<> / StructFrameBinding<> from arena_mappings.h to produce +// each VTX::Frame -- same PropertyAddressCache path as the FBS source. class ArenaJsonDataSource : public VTX::IFrameDataSource { public: - explicit ArenaJsonDataSource(std::string filepath) - : filepath_(std::move(filepath)) {} + ArenaJsonDataSource(std::string filepath, const VTX::PropertyAddressCache& cache) + : filepath_(std::move(filepath)) + , loader_(cache, false) {} bool Initialize() override { std::ifstream ifs(filepath_); @@ -310,7 +316,8 @@ class ArenaJsonDataSource : public VTX::IFrameDataSource { } const ArenaFrame& af = replay_.frames[cursor_++]; - out_frame = ArenaToVtx::MapFrame(af); + out_frame = VTX::Frame {}; + loader_.LoadFrame(af, out_frame, "ArenaFrame"); out_time = {af.game_time, std::nullopt, VTX::GameTime::EFilterType::OnlyIncreasing}; @@ -323,6 +330,7 @@ class ArenaJsonDataSource : public VTX::IFrameDataSource { std::string filepath_; nlohmann::json root_; ArenaReplayJson replay_; + VTX::GenericNativeLoader loader_; size_t total_ = 0; size_t cursor_ = 0; }; @@ -561,7 +569,7 @@ int main() { VTX_INFO("on every recorded frame; the persisted .vtx contains the post-processed values."); VTX_INFO("--- 1. JSON data source ---"); - ArenaJsonDataSource json_ds(writer_dir + "/arena_replay_data.json"); + ArenaJsonDataSource json_ds(writer_dir + "/arena_replay_data.json", arena_schema.GetPropertyCache()); RunPipeline(json_ds, reader_dir + "/arena_from_json_ds.vtx", schema, "arena-adv-json-0001", VTX::CreateFlatBuffersWriterFacade); diff --git a/samples/arena_mappings.h b/samples/arena_mappings.h index 8258255..18e37b7 100644 --- a/samples/arena_mappings.h +++ b/samples/arena_mappings.h @@ -1,55 +1,47 @@ #pragma once -// arena_mappings.h -- Arena game data model + JSON mapping for the samples. +// arena_mappings.h -- Arena game data model + mappings (JSON ingest + VTX slot output). // -// Two concerns live here: +// Two layers of compile-time reflection live here: // -// 1. ArenaVec3 / ArenaQuat / ArenaPlayer / ArenaProjectile / ArenaMatchState / -// ArenaFrame / ArenaReplayJson -// C++ types that mirror the arena replay JSON structure. +// 1. VTX::JsonMapping +// Drives UniversalDeserializer<>::Load(JsonAdapter) +// in advance_write.cpp. Walks JSON -> C++ struct. // -// 2. VTX::JsonMapping specializations for each of those types. -// Same compile-time reflection pattern used by real integrations -// (see tools/integrations/sf/sf_mappings.h). Consumed by -// VTX::UniversalDeserializer<>::Load(JsonAdapter) -// in advance_write.cpp. +// 2. VTX::StructMapping + VTX::StructFrameBinding +// Drives GenericNativeLoader::Load / LoadFrame in advance_write.cpp. +// Walks C++ struct -> VTX::PropertyContainer / VTX::Frame. // -// 3. ArenaToVtx::MapFrame() -// Hand-written bridge from the arena data model to a VTX::Frame -// (PropertyContainer layout matches content/writer/arena/arena_schema.json). -// This is the "manual" counterpart to the schema-driven ProtoBinding -// and FlatBufferBinding specializations declared in advance_write.cpp. -// -// Property-vector indices assigned here MUST match the field order in -// arena_schema.json -- see the comments above each MapXxx() function. +// The C++ data model uses VTX::Vector / VTX::Quat directly so no per-field +// conversion is needed between the JSON ingest layer and the VTX output layer. +// (This is the equivalent of the manual ArenaToVtx::Map* helpers we deleted -- +// the schema-driven StructMapping<> now does that work automatically.) #include #include +#include #include #include "vtx/common/adapters/json/json_policy.h" +#include "vtx/common/adapters/native/struct_mapping.h" +#include "vtx/common/readers/frame_reader/native_loader.h" #include "vtx/common/readers/frame_reader/type_traits.h" #include "vtx/common/vtx_types.h" +#include "arena_generated.h" + // =================================================================== // Arena game data model (matches the JSON data source structure) // =================================================================== -struct ArenaVec3 { - double x = 0, y = 0, z = 0; -}; -struct ArenaQuat { - float x = 0, y = 0, z = 0, w = 1; -}; - struct ArenaPlayer { std::string unique_id; std::string name; int team = 0; float health = 100.0f; float armor = 50.0f; - ArenaVec3 position; - ArenaQuat rotation; - ArenaVec3 velocity; + VTX::Vector position; + VTX::Quat rotation; + VTX::Vector velocity; bool is_alive = true; int score = 0; int deaths = 0; @@ -58,13 +50,16 @@ struct ArenaPlayer { struct ArenaProjectile { std::string unique_id; std::string owner_id; - ArenaVec3 position; - ArenaVec3 velocity; + VTX::Vector position; + VTX::Vector velocity; float damage = 25.0f; std::string type = "bullet"; }; struct ArenaMatchState { + // Default to the canonical id. The match-state JSON doesn't carry one; + // the FB/Proto bindings inject the same literal -- this keeps parity. + std::string unique_id = "match_001"; int score_team1 = 0; int score_team2 = 0; int round = 1; @@ -90,25 +85,24 @@ struct ArenaReplayJson { }; // =================================================================== -// JsonMapping specializations (JSON key → C++ member) -// -// Same pattern as sf_mappings.h. Consumed by UniversalDeserializer -// or any code that inspects the mapping tuple at compile time. +// JsonMapping specializations (JSON key -> C++ member) // =================================================================== +// Two new mappings for the VTX math types so the JSON ingest layer can fill +// them directly. Same shape as the JSON file: {"x":..., "y":..., "z":...}. template <> -struct VTX::JsonMapping { +struct VTX::JsonMapping { static constexpr auto GetFields() { - return std::make_tuple(MakeField("x", &ArenaVec3::x), MakeField("y", &ArenaVec3::y), - MakeField("z", &ArenaVec3::z)); + return std::make_tuple(MakeField("x", &VTX::Vector::x), MakeField("y", &VTX::Vector::y), + MakeField("z", &VTX::Vector::z)); } }; template <> -struct VTX::JsonMapping { +struct VTX::JsonMapping { static constexpr auto GetFields() { - return std::make_tuple(MakeField("x", &ArenaQuat::x), MakeField("y", &ArenaQuat::y), - MakeField("z", &ArenaQuat::z), MakeField("w", &ArenaQuat::w)); + return std::make_tuple(MakeField("x", &VTX::Quat::x), MakeField("y", &VTX::Quat::y), + MakeField("z", &VTX::Quat::z), MakeField("w", &VTX::Quat::w)); } }; @@ -137,6 +131,9 @@ struct VTX::JsonMapping { template <> struct VTX::JsonMapping { + // unique_id is intentionally NOT in this mapping -- it isn't in the JSON file. + // The C++ default ("match_001") survives because UniversalDeserializer + // skips missing keys. static constexpr auto GetFields() { return std::make_tuple(MakeField("score_team1", &ArenaMatchState::score_team1), MakeField("score_team2", &ArenaMatchState::score_team2), @@ -167,80 +164,78 @@ struct VTX::JsonMapping { }; // =================================================================== -// ArenaToVtx — arena game types → VTX PropertyContainer -// -// Property indices must match the field order in arena_schema.json. +// StructMapping specializations (C++ member -> VTX slot name) // =================================================================== +// Replaces the hand-written ArenaToVtx::MapPlayer / MapProjectile / +// MapMatchState helpers from the previous version. GenericNativeLoader +// walks these tuples automatically. -namespace ArenaToVtx { - - inline VTX::Vector ToVtxVector(const ArenaVec3& v) { - return {v.x, v.y, v.z}; - } - inline VTX::Quat ToVtxQuat(const ArenaQuat& q) { - return {q.x, q.y, q.z, q.w}; +template <> +struct VTX::StructMapping { + static constexpr auto GetFields() { + return std::make_tuple(MakeStructField(ArenaSchema::Player::UniqueID, &ArenaPlayer::unique_id), + MakeStructField(ArenaSchema::Player::Name, &ArenaPlayer::name), + MakeStructField(ArenaSchema::Player::Team, &ArenaPlayer::team), + MakeStructField(ArenaSchema::Player::Health, &ArenaPlayer::health), + MakeStructField(ArenaSchema::Player::Armor, &ArenaPlayer::armor), + MakeStructField(ArenaSchema::Player::Position, &ArenaPlayer::position), + MakeStructField(ArenaSchema::Player::Rotation, &ArenaPlayer::rotation), + MakeStructField(ArenaSchema::Player::Velocity, &ArenaPlayer::velocity), + MakeStructField(ArenaSchema::Player::IsAlive, &ArenaPlayer::is_alive), + MakeStructField(ArenaSchema::Player::Score, &ArenaPlayer::score), + MakeStructField(ArenaSchema::Player::Deaths, &ArenaPlayer::deaths)); } +}; - /// Player → entity_type_id 0 - /// string[0]=UniqueID string[1]=Name - /// int32[0]=Team int32[1]=Score int32[2]=Deaths - /// float[0]=Health float[1]=Armor - /// vector[0]=Position vector[1]=Velocity - /// quat[0]=Rotation - /// bool[0]=IsAlive - inline VTX::PropertyContainer MapPlayer(const ArenaPlayer& p) { - VTX::PropertyContainer pc; - pc.entity_type_id = 0; - pc.string_properties = {p.unique_id, p.name}; - pc.int32_properties = {p.team, p.score, p.deaths}; - pc.float_properties = {p.health, p.armor}; - pc.vector_properties = {ToVtxVector(p.position), ToVtxVector(p.velocity)}; - pc.quat_properties = {ToVtxQuat(p.rotation)}; - pc.bool_properties = {p.is_alive}; - return pc; +template <> +struct VTX::StructMapping { + static constexpr auto GetFields() { + return std::make_tuple(MakeStructField(ArenaSchema::Projectile::UniqueID, &ArenaProjectile::unique_id), + MakeStructField(ArenaSchema::Projectile::OwnerID, &ArenaProjectile::owner_id), + MakeStructField(ArenaSchema::Projectile::Position, &ArenaProjectile::position), + MakeStructField(ArenaSchema::Projectile::Velocity, &ArenaProjectile::velocity), + MakeStructField(ArenaSchema::Projectile::Damage, &ArenaProjectile::damage), + MakeStructField(ArenaSchema::Projectile::Type, &ArenaProjectile::type)); } +}; - /// Projectile → entity_type_id 1 - /// string[0]=UniqueID string[1]=OwnerID string[2]=Type - /// vector[0]=Position vector[1]=Velocity - /// float[0]=Damage - inline VTX::PropertyContainer MapProjectile(const ArenaProjectile& pr) { - VTX::PropertyContainer pc; - pc.entity_type_id = 1; - pc.string_properties = {pr.unique_id, pr.owner_id, pr.type}; - pc.vector_properties = {ToVtxVector(pr.position), ToVtxVector(pr.velocity)}; - pc.float_properties = {pr.damage}; - return pc; +template <> +struct VTX::StructMapping { + static constexpr auto GetFields() { + return std::make_tuple( + MakeStructField(ArenaSchema::MatchState::UniqueID, &ArenaMatchState::unique_id), + MakeStructField(ArenaSchema::MatchState::ScoreTeam1, &ArenaMatchState::score_team1), + MakeStructField(ArenaSchema::MatchState::ScoreTeam2, &ArenaMatchState::score_team2), + MakeStructField(ArenaSchema::MatchState::Round, &ArenaMatchState::round), + MakeStructField(ArenaSchema::MatchState::Phase, &ArenaMatchState::phase), + MakeStructField(ArenaSchema::MatchState::TimeRemaining, &ArenaMatchState::time_remaining)); } +}; - /// MatchState → entity_type_id 2 - /// string[0]=UniqueID string[1]=Phase - /// int32[0]=ScoreTeam1 int32[1]=ScoreTeam2 int32[2]=Round - /// float[0]=TimeRemaining - inline VTX::PropertyContainer MapMatchState(const ArenaMatchState& m) { - VTX::PropertyContainer pc; - pc.entity_type_id = 2; - pc.string_properties = {"match_001", m.phase}; - pc.int32_properties = {m.score_team1, m.score_team2, m.round}; - pc.float_properties = {m.time_remaining}; - return pc; - } +// =================================================================== +// StructFrameBinding (ArenaFrame -> VTX::Frame buckets) +// =================================================================== +// Same pattern as FlatBufferBinding::TransferToFrame +// and ProtoBinding::TransferToFrame in advance_write.cpp. +// The bucket/entity layout is game-specific; what gets pushed into each +// PropertyContainer is driven automatically by StructMapping<> above. - /// Full frame → single "entity" bucket. - inline VTX::Frame MapFrame(const ArenaFrame& af) { - VTX::Frame frame; - VTX::Bucket& bucket = frame.CreateBucket("entity"); - for (const auto& p : af.players) { - bucket.unique_ids.push_back(p.unique_id); - bucket.entities.push_back(MapPlayer(p)); - } - for (const auto& pr : af.projectiles) { - bucket.unique_ids.push_back(pr.unique_id); - bucket.entities.push_back(MapProjectile(pr)); - } - bucket.unique_ids.push_back("match_001"); - bucket.entities.push_back(MapMatchState(af.match_state)); - return frame; +template <> +struct VTX::StructFrameBinding { + static void TransferToFrame(const ArenaFrame& src, VTX::Frame& dest, VTX::GenericNativeLoader& loader, + const std::string& /*schema_name*/) { + dest = VTX::Frame {}; + VTX::Bucket& bucket = dest.GetBucket("entity"); + bucket.entities.clear(); + bucket.unique_ids.clear(); + + loader.AppendActorList(bucket, VTX::ArenaSchema::Player::StructName, src.players, + [](const ArenaPlayer& p) { return p.unique_id; }); + + loader.AppendActorList(bucket, VTX::ArenaSchema::Projectile::StructName, src.projectiles, + [](const ArenaProjectile& p) { return p.unique_id; }); + + loader.AppendSingleEntity(bucket, VTX::ArenaSchema::MatchState::StructName, src.match_state, + [](const ArenaMatchState& m) { return m.unique_id; }); } - -} // namespace ArenaToVtx +}; diff --git a/samples/content/reader/arena/arena_from_fbs_ds.vtx b/samples/content/reader/arena/arena_from_fbs_ds.vtx index fad0da87c898845af1b7a4da96c18744812d8f0f..4fbbb071a8a4d0777cfd87833b6764eb671d8860 100644 GIT binary patch delta 11460 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 ZHrl-LdB!?ihJzbm`DH;M*x8Uz{vYghIkNx& delta 11470 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 Yw?@1g)x-{)-r)Lf%*142ZmIMC0pLAqZ2$lO delta 11441 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𝔮QNA^#94@|MV*hbR~Wm)|vrZ@F)qjDAk_ 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 diff --git a/samples/content/reader/arena/arena_from_proto_ds.vtx b/samples/content/reader/arena/arena_from_proto_ds.vtx index a8361a57e7caadbc65026dc8415392e762966e35..e15308f9c4d504b21cdeb4d8d3b1158edca2a340 100644 GIT binary patch delta 24561 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 delta 24554 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 VTX slots" mappings. + * + * Twin of JsonMapping but the key is the VTX slot name (not a JSON key). + * Consumed by GenericNativeLoader to walk a C++ instance and push each member + * into the right PropertyContainer slot via the shared base's LoadField. + * + * Usage: + * + * template<> + * struct VTX::StructMapping { + * static constexpr auto GetFields() { + * return std::make_tuple( + * MakeStructField(ArenaSchema::Player::UniqueID, &ArenaPlayer::unique_id), + * MakeStructField(ArenaSchema::Player::Health, &ArenaPlayer::health), + * MakeStructField(ArenaSchema::Player::Position, &ArenaPlayer::position), + * ...); + * } + * }; + * + * @author Zenos Interactive + */ + +#pragma once +#include + +namespace VTX { + + /** + * @brief A single mapped field linking a VTX slot name to a C++ member. + * @tparam Class The struct that owns the member. + * @tparam Type The member's type. + */ + template + struct StructField { + const char* name; + Type Class::*member_ptr; + }; + + + /** + * @brief Helper to build a StructField with template deduction. + * Usage: MakeStructField("Health", &Player::health) + */ + template + constexpr auto MakeStructField(const char* name, Type Class::*member_ptr) { + return StructField {name, member_ptr}; + } + + /** + * @brief Mapping trait. Users specialize for their types. + * @details The specialization must provide a static `GetFields()` method + * returning a tuple of StructField objects. + */ + template + struct StructMapping; + + /** + * @brief Frame-level binding trait. Users specialize with a `TransferToFrame` + * static method that builds VTX::Frame buckets from the source struct. + * + * Equivalent to FlatBufferBinding::TransferToFrame and + * ProtoBinding::TransferToFrame. + * + * Usage: + * template<> + * struct VTX::StructFrameBinding { + * static void TransferToFrame(const ArenaFrame& src, VTX::Frame& dest, + * GenericNativeLoader& loader, + * const std::string& schema_name) { + * auto& bucket = dest.GetBucket("entity"); + * loader.AppendActorList(bucket, ArenaSchema::Player::StructName, + * src.players, + * [](const ArenaPlayer& p){ return p.unique_id; }); + * ... + * } + * }; + */ + template + struct StructFrameBinding; + + template + struct HasStructMapping : std::false_type {}; + + template + struct HasStructMapping::GetFields())>> : std::true_type {}; + + /** + * @brief True if T has a registered StructMapping specialization. + * Used by GenericNativeLoader to decide between recursive Load (struct mapped) + * and direct LoadField / FillFlatArray (primitive or VTX-native type). + */ + template + inline constexpr bool has_struct_mapping_v = HasStructMapping::value; + + +} // namespace VTX diff --git a/sdk/include/vtx/common/readers/frame_reader/loader_base.h b/sdk/include/vtx/common/readers/frame_reader/loader_base.h new file mode 100644 index 0000000..405806a --- /dev/null +++ b/sdk/include/vtx/common/readers/frame_reader/loader_base.h @@ -0,0 +1,234 @@ +/** +* @file loader_base.h +* @author Zenos Interactive +*/ + +#pragma once +#include "vtx/common/vtx_property_cache.h" +#include "vtx/common/vtx_types.h" + +#include +#include +#include +#include +#include +#include +namespace VTX { + /** + * @brief CRTP base for the "source-format -> PropertyContainer" loader family. + * + * Shares between GenericFlatBufferLoader / GenericProtobufLoader / + * GenericNativeLoader (and future siblings) the format-agnostic plumbing: + * - LoadField(dest, struct_name, field_name, value) + * - LoadBlob + * - AppendActorList / AppendSingleEntity + * - Internal helpers: StoreValue, EnsureSize, PushToFlatArray, FillFlatArray + * + * The Derived loader MUST provide: + * - const PropertyAddress* ResolveField(int32_t entity_type_id, + * const std::string& struct_name, + * const std::string& field_name) const; + * - template + * void Load(const T& src, PropertyContainer& dest, const std::string& struct_name); + * + * Everything else flows through this base via CRTP. No virtuals, no vtable. + */ + template + class GenericLoaderBase { + public: + template + void LoadField(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, + const T& value) { + const PropertyAddress* address = AsDerived().ResolveField(dest.entity_type_id, struct_name, field_name); + if (!address) { + return; + } + + const int32_t idx = address->index; + switch (address->type_id) { + case FieldType::String: + StoreValue(dest.string_properties, idx, value); + break; + case FieldType::Int8: + case FieldType::Int32: + case FieldType::Enum: + StoreValue(dest.int32_properties, idx, value); + break; + case FieldType::Int64: + StoreValue(dest.int64_properties, idx, value); + break; + case FieldType::Float: + StoreValue(dest.float_properties, idx, value); + break; + case FieldType::Double: + StoreValue(dest.double_properties, idx, value); + break; + case FieldType::Bool: + StoreValue(dest.bool_properties, idx, value); + break; + case FieldType::Vector: + StoreValue(dest.vector_properties, idx, value); + break; + case FieldType::Quat: + StoreValue(dest.quat_properties, idx, value); + break; + case FieldType::Transform: + StoreValue(dest.transform_properties, idx, value); + break; + case FieldType::FloatRange: + StoreValue(dest.range_properties, idx, value); + break; + case FieldType::Struct: + StoreValue(dest.any_struct_properties, idx, value); + break; + case FieldType::None: + default: + break; + } + } + + void LoadBlob(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, + const void* data, size_t byte_size) { + if (!data || byte_size == 0) { + return; + } + const PropertyAddress* addr = AsDerived().ResolveField(dest.entity_type_id, struct_name, field_name); + if (!addr) { + return; + } + const uint8_t* bytes = static_cast(data); + for (size_t i = 0; i < byte_size; ++i) { + dest.byte_array_properties.PushBack(addr->index, bytes[i]); + } + } + + template + void AppendActorList(Bucket& bucket, const std::string& schema_type, const SrcIterable& src, IdFunc id_func) { + if constexpr (requires { src.size(); }) { + bucket.entities.reserve(bucket.entities.size() + src.size()); + } + for (const auto& item : src) { + ExtractActor(item, bucket, schema_type, id_func); + } + } + + template + void AppendSingleEntity(Bucket& bucket, const std::string& schema_type, const Src& src, IdFunc id_func) { + ExtractActor(src, bucket, schema_type, id_func); + } + + protected: + template + void FillFlatArray(PropertyContainer& dest, FieldType type, int32_t idx, const It& src) { + using ValueType = std::ranges::range_value_t; + constexpr bool b_is_string = + std::is_convertible_v || std::is_convertible_v; + for (const auto& it : src) { + if constexpr (b_is_string) { + if (type == FieldType::String) + dest.string_arrays.PushBack(idx, std::string(it)); + } else if constexpr (std::is_arithmetic_v) { + switch (type) { + case FieldType::Int8: + case FieldType::Int32: + case FieldType::Enum: + dest.int32_arrays.PushBack(idx, static_cast(it)); + break; + case FieldType::Int64: + dest.int64_arrays.PushBack(idx, static_cast(it)); + break; + case FieldType::Float: + dest.float_arrays.PushBack(idx, static_cast(it)); + break; + case FieldType::Double: + dest.double_arrays.PushBack(idx, static_cast(it)); + break; + case FieldType::Bool: + dest.bool_arrays.PushBack(idx, static_cast(it)); + break; + case FieldType::String: + dest.string_arrays.PushBack(idx, std::to_string(it)); + break; + case FieldType::None: + default: + break; + } + } else if constexpr (std::is_same_v) { + if (type == FieldType::Vector) + dest.vector_arrays.PushBack(idx, it); + } else if constexpr (std::is_same_v) { + if (type == FieldType::Quat) + dest.quat_arrays.PushBack(idx, it); + } else if constexpr (std::is_same_v) { + if (type == FieldType::Transform) + dest.transform_arrays.PushBack(idx, it); + } else if constexpr (std::is_same_v) { + if (type == FieldType::FloatRange) + dest.range_arrays.PushBack(idx, it); + } else if constexpr (std::is_same_v) { + if (type == FieldType::Struct) + dest.any_struct_arrays.PushBack(idx, it); + } + } + } + + void PushToFlatArray(PropertyContainer& dest, FieldType type, int32_t idx, + const PropertyContainer& temp) const { + switch (type) { + case FieldType::Vector: + if (!temp.vector_properties.empty()) + dest.vector_arrays.PushBack(idx, temp.vector_properties[0]); + break; + case FieldType::Quat: + if (!temp.quat_properties.empty()) + dest.quat_arrays.PushBack(idx, temp.quat_properties[0]); + break; + case FieldType::Transform: + if (!temp.transform_properties.empty()) + dest.transform_arrays.PushBack(idx, temp.transform_properties[0]); + break; + case FieldType::FloatRange: + if (!temp.range_properties.empty()) + dest.range_arrays.PushBack(idx, temp.range_properties[0]); + break; + case FieldType::Struct: + if (temp.entity_type_id != -1) + dest.any_struct_arrays.PushBack(idx, temp); + break; + default: + break; + } + } + + template + static void EnsureSize(Vec& v, size_t index) { + if (v.size() <= index) + v.resize(index + 1); + } + + template + static void StoreValue(Vec& vec, size_t index, const V& val) { + EnsureSize(vec, index); + using VecValueT = typename Vec::value_type; + if constexpr (std::is_same_v) { + if constexpr (std::is_convertible_v) + vec[index] = val; + else if constexpr (std::is_arithmetic_v) + vec[index] = std::to_string(val); + } else if constexpr (std::is_assignable_v) { + vec[index] = static_cast(val); + } + } + + private: + Derived& AsDerived() { return static_cast(*this); } + const Derived& AsDerived() const { return static_cast(*this); } + + template + void ExtractActor(const Src& src, Bucket& bucket, const std::string& schema_type, IdFunc id_func) { + PropertyContainer& entity = bucket.entities.emplace_back(); + AsDerived().Load(src, entity, schema_type); + bucket.unique_ids.push_back(id_func(src)); + } + }; +} // namespace VTX diff --git a/sdk/include/vtx/common/readers/frame_reader/native_loader.h b/sdk/include/vtx/common/readers/frame_reader/native_loader.h new file mode 100644 index 0000000..96d9acf --- /dev/null +++ b/sdk/include/vtx/common/readers/frame_reader/native_loader.h @@ -0,0 +1,173 @@ +/** + * @file native_loader.h + * @brief Loader that walks C++ struct instances using StructMapping + * and pushes their members into a VTX::PropertyContainer. + * @author Zenos Interactive + */ + +#pragma once + +#include "vtx/common/adapters/native/struct_mapping.h" +#include "vtx/common/readers/frame_reader/loader_base.h" +#include "vtx/common/readers/frame_reader/type_traits.h" +#include "vtx/common/vtx_property_cache.h" +#include "vtx/common/vtx_types.h" +#include "vtx/common/vtx_types_helpers.h" + +#include +#include +#include +#include +#include + +namespace VTX { + + /** + * @class GenericNativeLoader + * @brief Twin of GenericFlatBufferLoader / GenericProtobufLoader for the + * "source is a plain C++ struct" case. + * + * Inherits from GenericLoaderBase via CRTP. Implements the two required hooks: + * - ResolveField: maps (entity_type_id, field_name) -> PropertyAddress*. + * - Load: walks StructMapping::GetFields() and dispatches each field + * to LoadField / LoadStruct / LoadArray as appropriate. + */ + class GenericNativeLoader : public GenericLoaderBase { + public: + explicit GenericNativeLoader(const PropertyAddressCache& cache, bool debug = false) + : cache_(&cache) + , debug_mode_(debug) {} + + /** + * @brief Resolves a (struct, field) pair to a PropertyAddress via the cache. + * @details struct_name is unused here (the cache is keyed by entity_type_id), + * kept in the signature for symmetry with the Proto loader and for debug. + */ + const PropertyAddress* ResolveField(int32_t entity_type_id, const std::string& /*struct_name*/, + const std::string& field_name) const { + auto struct_it = cache_->structs.find(entity_type_id); + if (struct_it == cache_->structs.end()) + return nullptr; + auto prop_it = struct_it->second.properties.find(field_name); + if (prop_it == struct_it->second.properties.end()) + return nullptr; + return &prop_it->second; + } + + /** + * @brief Load a single C++ instance into a PropertyContainer. + * @details Sets dest.entity_type_id (if not yet resolved), iterates + * StructMapping::GetFields(), dispatches each field, and + * computes the container hash at the end. + */ + template + void Load(const T& src, PropertyContainer& dest, const std::string& struct_name) { + static_assert(has_struct_mapping_v, + "GenericNativeLoader::Load requires a StructMapping specialization."); + + if (dest.entity_type_id == -1) { + auto it = cache_->name_to_id.find(struct_name); + if (it != cache_->name_to_id.end()) { + dest.entity_type_id = it->second; + } + } + + constexpr auto fields = StructMapping::GetFields(); + std::apply([&](auto&&... f) { (ProcessField(src, dest, struct_name, f), ...); }, fields); + dest.content_hash = Helpers::CalculateContainerHash(dest); + } + + /** + * @brief Load a top-level frame struct into a VTX::Frame. + * @details Dispatches to StructFrameBinding::TransferToFrame, + * which the dev writes by hand (it owns the buckets/entities layout). + */ + template + void LoadFrame(const FrameT& src, VTX::Frame& dest, const std::string& schema_name) { + StructFrameBinding::TransferToFrame(src, dest, *this, schema_name); + } + + /** + * @brief Load a nested scalar struct member into dest.any_struct_properties[slot]. + */ + template + void LoadStruct(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, + const NestedT& src_nested) { + const auto* addr = ResolveField(dest.entity_type_id, struct_name, field_name); + if (!addr) { + return; + } + + EnsureSize(dest.any_struct_properties, addr->index); + Load(src_nested, dest.any_struct_properties[addr->index], addr->child_type_name); + } + + /** + * @brief Load a std::vector (or compatible range) member into the right flat array. + * @details Primitives / VTX-native types -> base's FillFlatArray (push direct). + * Elements with StructMapping<> -> recurse per element, then push the + * resulting PropertyContainer to any_struct_arrays (Struct case) or + * collapse to a flat math array via PushToFlatArray. + */ + template + void LoadArray(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, + const Container& src_array) { + if (src_array.empty()) { + return; + } + const auto* addr = ResolveField(dest.entity_type_id, struct_name, field_name); + if (!addr) { + return; + } + + using ElementT = std::ranges::range_value_t; + const int32_t idx = addr->index; + const FieldType type = addr->type_id; + + if constexpr (has_struct_mapping_v) { + const std::string& child_schema = addr->child_type_name; + for (const auto& item : src_array) { + if (type == FieldType::Struct) { + PropertyContainer child; + Load(item, child, child_schema); + dest.any_struct_arrays.PushBack(idx, std::move(child)); + } else { + PropertyContainer temp; + Load(item, temp, child_schema); + this->PushToFlatArray(dest, type, idx, temp); + } + } + } else { + this->FillFlatArray(dest, type, idx, src_array); + } + } + + private: + /** + * @brief Per-field dispatcher called from Load. + * @details Looks at the C++ type of the member and routes: + * - std::vector -> LoadArray + * - has StructMapping -> LoadStruct (recurse) + * - everything else -> base's LoadField + */ + template + void ProcessField(const T& src, PropertyContainer& dest, const std::string& struct_name, const Field& field) { + const auto& val = src.*(field.member_ptr); + using V = std::remove_cv_t>; + + if constexpr (is_vector_v) { + LoadArray(dest, struct_name, field.name, val); + } else if constexpr (has_struct_mapping_v) { + LoadStruct(dest, struct_name, field.name, val); + } else { + // Primitive (int, float, bool, std::string) or VTX-native type + // (Vector, Quat, Transform, FloatRange, PropertyContainer). + this->LoadField(dest, struct_name, field.name, val); + } + } + + const PropertyAddressCache* cache_; + bool debug_mode_; + }; + +} // namespace VTX From 76f01af8d55e6ceee02651f2f44856e7afe28318 Mon Sep 17 00:00:00 2001 From: Alejandro Canela Date: Wed, 13 May 2026 17:40:41 +0200 Subject: [PATCH 2/4] refactor(common): migrate FB and Proto loaders onto GenericLoaderBase --- samples/advance_write.cpp | 4 +- .../reader/arena/arena_from_fbs_ds.vtx | Bin 1783327 -> 1783336 bytes .../reader/arena/arena_from_json_ds.vtx | Bin 1783327 -> 1783336 bytes .../reader/arena/arena_from_proto_ds.vtx | Bin 1887298 -> 1887222 bytes .../readers/frame_reader/flatbuffer_loader.h | 282 +++--------- .../readers/frame_reader/protobuff_loader.h | 433 +++--------------- 6 files changed, 120 insertions(+), 599 deletions(-) diff --git a/samples/advance_write.cpp b/samples/advance_write.cpp index 70f6139..4fd7ac7 100644 --- a/samples/advance_write.cpp +++ b/samples/advance_write.cpp @@ -168,8 +168,8 @@ namespace VTX { [](const ::arena_pb::Projectile& projectile) { return projectile.unique_id(); }); if (src.has_match_state()) { - loader.AppendSingleActor(entity_bucket, ArenaSchema::MatchState::StructName, src.match_state(), - [](const ::arena_pb::MatchState&) { return std::string("match_001"); }); + loader.AppendSingleEntity(entity_bucket, ArenaSchema::MatchState::StructName, src.match_state(), + [](const ::arena_pb::MatchState&) { return std::string("match_001"); }); } } }; diff --git a/samples/content/reader/arena/arena_from_fbs_ds.vtx b/samples/content/reader/arena/arena_from_fbs_ds.vtx index 4fbbb071a8a4d0777cfd87833b6764eb671d8860..d57d61463e40f9c02ab3d67fddd5946108c69031 100644 GIT binary patch delta 11930 zcmXApc{mmC!^RQDQrVpfA!Q^bR7W9uLuFqMileBpMUhU3AOwmRdEd<@f^{yyQYgsrUM_3sBKtBcHMCfr7Oyz@isN7jyZ z#_v`56NTuB8iZ>QdO2E^Lp25ucu}tz8*H-6^vDLKZh=7$yyCf7zrm70vfp5-O^cvY z8)!|4jOts^_^Tl4W|RzXhk6B?eSfrh*UoDOi&K72fXcgad)H6eOtpHP z{`x|P>F94P`vB=UZ)j+~%jfWt{^#AXEtPD3FRGHO zVr8l)yMw+|xc!pyNPYHdz^cMfK`}!5%f5=3gK2MG(7pv4t-o>E!N067Y3c4&Q0U7x z>3}Qn@4S%y`^_+azOc+MYo&bDQ|+CERof}w^PRCtH}{PP$_RhD5v=~6AN}-pxymuQ zr<$wtIP$sVZK>ZYxe4RUI*a++(oUSM+Jo8s!-uZt#`0dJ!)BGbHvr?v>`QT&*iqtP zeW_%}ThHZPbRY8Y*}h`iI`UF1?w-f;3z@@*6u0>P^)0E33WPbmPZ-b{tK3vvpvC{= zTObFwea~IYd8MT-r0q1$vdLG|%6+e?y!27Na5Hc=Vs{+*s^hDs;7ybBvQ`bL-L^C{ z|Kp#H8VUxy6+Vuh?=1?evwo%!usZu`s1>YA-YRVT23X@l8tJ~#=6aa z{~g<~Z^N+-=QhALT-$JO!?O+VHaoZ3waxBr__pESMqnGkZG^TF-bQ2_(QOFZ?Ac~7 z-I8tJ$OQW}gsp^j^83Dj2zWZnpHE4XMX5DM-Q+3sxxnn&v&##`a6s8D&Wd{pRaEhp z3204*@@urdzQUlXuD=;`c>8`D8@~Mx-|G4u)%#E61CTQypuiBP4}m(vO5LOCHsjF5 zgt_PU%d0=&uvu@uxqFWnsE3vkpRZ)7{YBf>D$F+Ohqo}8E#L$4n|&k@z`1ySC++;V zpARS!5IiX?-XscM*sJ_WO#RA!=%^l$;_cZT5xc#*5hSn#ZwM27`vX{|hFqwaReV8z}d*wLQX$tCrg zx3o)Oi5r!8BdHnj$~5vW9K2oWM5&+i!~E`jU`IydLVoNETgs2vIR1dEE?N+JDDfd) z&OS-yM~a5eBZJMy<_|LMg|p$T+*|Vbfz{{(t)isPl1%GzbiAU<<3&C5Wyk$j{k!T$ z9yQ=HZ?KBA3&(s^z6CZ+QVsE> zf$2>0PjqIk!gsNrWf_b9(=WO@ma{&su(?EMxwoOSgS&@A$d*fDk_YtKrLxAS5iSVv zi}B`4R*3tkOCp%`1M`DPBeIz;ho3DSsR}w)&#i484 z;%4XwUDZb$pnr18+iaId+=Q%ieA5?pKRqklQD&w+aBO3HVqtXtQg{d1|J zx;F}Xc4Xe{m-88^@|`|=cd5gF!!i)~iU_&im-rKQID~p~XOt!_#_&L#MQH+AHQBW# z)yFt3a47wO^OL0c>@5F0l%t?3zVNkJac4nkztZzDq^@!ns$Tj)-?Fa-rt5@mH%e~3 z0i#+~L@>?l4(Ldi#mgSD_IvlPejm~&Iv&1F4BUz4D@(7{msN~7xN1BvXE}Yc-M1-!KA^lV*Q#9S#bbMm#d>D+UW-OM>&_{g{+pM^*6k5I!jUQ9w3TsGyjy!Ja-Fm&x3hy_KKK zr<6~qLnWai@IwjFzFhN11?2O>Wb-BY!7DG}I!aapBL9Y3-9po9%j#?|N31*P7KCQFBWr-8oXka^;c4{Ug|YB6q6No9Mdot|Ooe@0ba6melF%b9X?qZylqGr{ z)?7Pc5`K)tuS8B)ff3nLlnPZuT`dmNpqXoBjp(2*`t&8JG3Y$TZNx}68Iv@hFS2Bw zByBa>0v9h67!HzGuYkXthS zYlIl}Ra0aF%APVg@&kT3{ar;v|Iv4EHKwr!V+Yw8<(y+@ zxbe3L@4V@5V3?l|79#u>kqq3U$R$QhlF-~QW%BF*>BJ#&BM6_Dr+idE92KdvN^y5i zpsd95nA3EEdSmX{-lN)#8r?D7bMrk0Oxp`vlSaTzQ^LBrWP}wcV568}2Z8n`m1L5} zRdTztmjz52bwyk~;+8#W$gM1%+i1!igf8XRSR8BUQm!Ep!1yt{6aT~?7$g(VFkU6JlPT`zfnZ)G(PiK3UphxvWD+%AX&ls~vA_Q^w3rGKPs`@EQEK`h1`! zz^%iVtVfW9z#>D%lNX6dldLe-Yoc z%zX4`OJ)@)Uza?y3BF~yuV}i1_=N+y&SkR5L%O$%4Dfp;3Q{CQkU|1gWgo3UJj+lL z9guD$BL}e=*F1E`q;dv5;I3-OW_n>jVeJ`O|nNr(N>E%siWzo53NDu@bfUg zkypA2Mb?~JVHu}RqG7gK=9kMy9Oy1r8kd}UgRV2UUB{B$aY@guqMN`eAHFxfgo}4U zhQH#~K;kb%^X`3<9pR*hRPx>^uct8-#W)11O`z&0XZ59`m(t3=r_*me!Tx1a^B95! znT1Tb;;K?W^Eu&NrByZfmF`|k{8I;oH12FB(OTgHSZN1EwX3CvX8eArA9emjpC9!9 zf^m$-e`SbG;004kKbUnhfWfc+d4j|5>1E<==GH0{^^YX7mCYIoA7OvVNzq2Qsa?FJ z-L!A~=nbL2BJ|Kb*lsaKn#4gVy!-(3%%Qg+U?xATK!6p&-%5ceh+IS{>GXbe(zCPh z32jQF?)h`nj|Mcy3$sS(9aB~ddW;oDu*tP!9JR-5$hudVJe{L5+iO2 z+=eplfPSRP09YfaJ(z-6gi=StT_b7BQ795i=ZQ~A#H3Of#k5n8@i!S4vzUw=!0G8+ z0m1JX$X*gxM%-5cGy zVVZn8=wrP@5dFEK+C{9*7&kK;wqU~6zioh^OWY2GWJgfaiCA>)qzlyKcHsu;lh+k* z_~&heN};g%(_-)KLD0OAV`21H5t!ZsMsKv;LwqX!W)gEfCE^hvm=0!U%4HL)a?j>N z9jIjyX{-cxE2pef1izs2(o(;UCjJKPj7Y4;B%s@N(84n*1tSy-wHK>|_bYtapraFg&=JJox=x zz;6CDLBc_iasqf}-&=8_sU-A8nsoi(qAYyxFrYw5JSL$;EmT2OPBo~~4b=uTFtXN+ z4#QU;XE9(#pBFU(a!rn!6KX7VNnnpHWP5qif#h{%%?S>_&hJV|cc&tOLsu zYkrqL;_niOEg^W&eeQ5(GF37PD2_Q5M|hI}UQ8x3Qm>{#ztZnMA??U{m`B-LKz&-M zSWK%e)qjrmRbHy5f2U)&Y8n6PsExQ_b7m`34y)=QXm)|`daT|PzxKO-g8mGKd?D=| zg=t?Y2PR6VsH#7ZmKmDyuc3Lm^KWc^*`LYaSd0IMi)|IKMlhAwfqG7Y0e3$y=&*a5 zpLknnON11)2Nn^_mY^JwdU=4VeW(khk>t^D3iKO_f0eM%6T69ww9^OGndN5zHSIQC zg4wxY0}#IO+lUxw3UOJGlC1XIz|ZVX*i#zG=daQ}BF?BIJnKro!c_RpNaA`F6cH;BPs&IHQ{YOP#$!r*2Gt^qHk#x56kRSrp3!+q zQpy-o6?k#gsh7+*H5Xq4gY`~Lgt=zW?=6wNJ?XZsn_-{h|vP}b4t?_WH51ARF0v1r2Z&waJ*j`AfKEe5`53FXn@h0qB_JJJq3tF zH-vRAb{kV{%qA?Tp4O{2XxJq_2Za9E@sJbt{F<5zqs`6i26Na8aPeNcO$efJ`-92% zBoU}6^H?_aDpxNb??vs3m{TP;%YlCt z)E5Lndgd#lTpd)^aP|$Uqs6if9&2~&q^@)a_tAL!Q$M28gC(Eo#1W)p)Olx1K zIKx_UTpIT{$dqN?KfFtUkai3_s8p^(Jag);DrBnmMFYOBwWve6rwrLazH!Pq5~ut`D)tmt=c)(jWE;+}^10`~2b5 zbgFC=S`nijN5>M(ld+Lhmo&ywIy2}AcMgz}CsjZwE<9BXzA3%<9AZ>nttS1V->rpr z)IV&b>}{q!ZB@k3+7A6LdSB0__t^LTTc7ZMgVZkwQ*bo%D{y$?#S}sF2l3sE)i3Dl zy!&s`pXCrHymO8Ak9uIMl=T5kmA!=%HRc}Tr91Ct%=7yT;T)pzdzfNk1rmU=RQ&pDUqU1P(+B?r>RHOU!J9DYj^3Qq;ua4up1Zt8ZkmmcU$0TRtIgE z<%k_Y&AyEcnq3`sCc^M&z*=}*l;E+K$P~N3AKE4TNQNXMTP6pe zK0;|dN;NtDS($e2!5nLGE{HKG`QGr43NzTGZqNwYt3RyjJ_o5 z0OdF;IFabrC^|0PZd98a6J9h=@73FA7=_N~k3GJ32w^-AQ47P{BFr8zhocED50~P> zpd{{8V)7%&bf_p3KAGK=dm;Z5O1)C_vxHL4R*A;G!1mDdULC7@)qv~0>1_cnwS8~D z)%mX*r1lB+XMU7J2BE6Y@Y#`$G0SgblT=fQ(UP`Sw&^S73!B>0Q%^~=;n2jt!rz&Y_cD&bn_uHuO`+%)vT z2YktQXZ(Ew0oLG{`ye4amwGg+CI-@t>q)Rph9^_K(x_|c;ZOK;((_Q+f{H>+y%;Ms ze?C%)yHqdHgK7bu`jkdcs=2uJ6!xYAy4c0&fv>**)qnTXjv?y9FMCIyenk~0YNs&$ zAAQKorC;CY@ms(DEd!BEp|vOf4sU@kSYtHV-*G}#++TTN_uYT^LxgsUQfYe*h@qtt zs!}Znj1OT$ptJnE0`9NKp$sIP5GSIimDR!evj*Bwzplf%X@lDr;4PylQxOYlw$%|E z^rfA)eHWQ@75nCV1IGWlhI;JwO1lLdLT*35qvi*;1(*dv!@+Rq@9@A#m@7IdmbyRw zS>lP5MjCql@y86uENnLC&QqKfjd?~W$t?qpRlKSK^rhi*2lH%GjM1==&- zQNixYUX2g!1E>XKbO>`DULHlpc_#6c?^4snvjB0fc>y$98eDPuJGTb;Z6NF{v2YyQ zK8}3gI1lyp&U1Y41<=dFxG3hmcS9_4zmPQkMCPz8P%Wo*r1Pltad2GO<0O*^-9NKS z1Ae3_qf@4L8ltuunp{Lb8(%Y9un4fmIBgRyi96t^qp}mwaLv$Vz>RzZobmGYhFE-~ zDWd*4_h1Et9-@T$S10S58mJfEd}_JU_Ol(Obh7ot_U-A%@;)9L#9w^|^hf%}E`9qx3Esm0 zO;djg&O@1ta?4eJ&aT28>z13;F_wp@+m02EU@l%>EOnPOKT;w{6v3MbM*9ZEog{&I zY5#*D2MEO<7E>rVrlbVdsX(VbsM@Phe`xq=d|kwUCZ5}GoEj6!lpjdFTU6BF8bdKM1kPM`x4=WR7@qRA;vInAOR;Q z&!hr((m;eYBjyPqCpQl|T2ND{TijC$+diMH^r~K?Q^RZd>(TT^Ot!hARUN}RaPzK_ z9+&q^{Q&ZbXDH>1)F@c|_0$CPW(vOegE4dU*ROf%-QPP_9y0f?p-=xQZq>5J>9b>f zoR_%q@4SHb?hXD(At6!l$)3YvFC;XjpmztXkV9WVcX{}aLWm-Dr!q=Aae!EQT2&ou zIcuyvr0aYRpEvNoz+oJ33W!^vR-m#CRBvZs-%ob93Qs%VhPPa!Jg6dG*|(0|et8Gg z_Uj73NJ03w;2WWT!$TthzUarXhvJ_nf@&#kG_%LU84#THI|mMYO65Y6p6xGrR)(M_ zDjKWKzx-H(Ili8)zthCpg2%iiv;%qXz+>I7di6f^4nTH{siB+0>!WbQxWHt_cW|0o zIjb>;wl827OQS2Uf0x&AWP^t#K7~z+11JHAJk2|e_`pE{Ct+w#6!zQ8E*`gkpENaJ z=D6%@xpPO*_eU=u$CbTLVjDyx@(eDd0c2^)>s0G$L13q$^~G^x4>O2qao>8E?W0Su zj6<0t^|TY(ddB*n(GpH>Ym`1sC^6?(A(=(|$+1qCbBHsMlc)=5I!j7SN?_sXeHZy9Y|{ zgC+ZmK2lE(HhsP@@@Wjc^6lp&1!tSVVt?+L&s#jUjKBH==&$yzgO@hHvn1Tw@s9(d zatZQg?vmq&s|3%AbPz1}QOCqlH^~)gEchTVn0i=R0WUd5RBBc+It2`=I;+iV_-lb2 zx=_5nm;qdH9#Jx?Gl9&hA1v)jKWu$2qni#7t_VA4UB~2I@oIN1PoV22$>*Ezjl1Aq z|IonQ!D;uQgW=`WGf{72VADA2mxSxd=wj-35A50sLh&r||; zszKJ8m|BQXpWAq}xuzA?#d)y1ki^=5jwBfJ1?&tm(gj}HRG)8Hr2Q08sD^;5oJ7ar% z2q8OPop<(!0Zz9B4{W0s01{4 z(Dt2}1>dLu?kc=2itObP2U7P-OP9zHWx-}Sqa%YyosL6u%CO%_c2#QJnSC1hn#Xm} z*Lvrm_lB1*Vz{xl*@i`=H6U!8bxGabz#{0*$+Q++cd$_l9>NzKQW{+@TAA~Lsz9f| z(y8liKyBVkw0O3yw&P))d_9l*0NMVEkLrWiXV83PWX$E;(j*jwbI&CIl$@s)EuLI% z`h#9r{j`2%^CwF(hER5}amI4(;l=ZI9pir`s3!vU5iaffE)Lz2{3lI4Cg%)c2rjkDGo%wCEQ5UiJTOWNOcZ=C}-3?eTG8ADG@ z4_myj(zJo!*;(0tCA(jx{&5b0ce>F$umfJDw^VQAEq9Flh60>}fQ8_IP|k>iNKiZ) zja81XPlOFp`e_c2snZ#^v$k@go}waX_Ol};$jdTJyP~U#^zvH`;Q4yBKCB7kYkBXh-d5vrO`?m4cT@%izE2xQP<wUIp{-xs%uN}`hVehY9cER1eZ{QnVk={bS zU=~H*zxtjQ0(FL1hmA*gJb;ik)w4iigrbj%WQBdyrt!Iyc- zsaK?(fzs=$t}X7yp5V|;XP{y~Z29 zn_mD+7m^jN*aK*YwM$q?jUE794=sa;JkL=mMNvw*_{1q9{O0sUb;eors`fA4yXSTo zVh=CuHGXQUXo1&S>D%<#U9tyoviH>uXDAXDa(m)&*b9Dv+|s=L?v9lo^=p88(4XLt zP;_SmE%HD#UK*Z3OZHprNc+oo+TC5fIO)T;fg22Pbtr6@Z|w0nbZGMVceUxZS=elDcmZDey+RHA z%e9`gf$c|F(w?!M;AjN!^E@ASI`Yj502E<1LhRl>;$YtXW75zo8CXxYSI+Lp)KTiq z7#O)%CZ;eX?p7`%%l$g+;Hv9LQ6m!0CXgFO7s-=fhzqC9}NcwK11Y@ znK9V+8M|s}t*HEUEry{qCxF>eIu~X z_K(KdL(S6CJ%TWS_8x7`H$l1in54u#R+vH*nZkV zee|rW_OdQ=j>iyAxgcd+Y8@GQ+_!}r zOMh3uu)lokk2l~$EE&())HvDzGoIm{FyC(hG*Fm}khB-yFaB)*3F$_e^Rgf19FNQ% z1u4hbRANu=QO!GZOyiX%tf$kfX9rCgqBk$D8%LN4SY}v*ww1^w4F|m4(ZXr;8tCS- z;ud^^_hzcMv~LN8=npsFGeQPKoWkZJ{2s8!pm7iPCFCa^PsLw9I+y-F^Kv$x>z%)W zMizm>rCH_jm2mY7EqdoG>$>p{aCzVlOkSN?_uWKUSTmz{h;rs|De%&F>F{?8;x-}^1kZh|;$f0} z2Ob{;WxI&E~*j#Bu#pTqJYLFWD*<>wAU9?F5q(Rep{DRRZlPl&wEh!{6yV%Qn z$l%i}iq5sy^5N6vHjy^!|%;7U0YJ+t=qpXt^Y)+g857dh{7f`{(Xd-8DJMRfcavo`7aMdFhYW6^ci)d4q;@ysAgpUq=cx1q})@4C}DmcrBX)1k6` zwxtSTCQgdpg*!#YPnyJ98mJ7e7%Xb0Am%YU2)WRe#PYveH=OcxzmvboQZsyKSt0a(c(9 ziwehQJC~&PC|@kJfJ{pZqDKo}`UJj=A{AQAB#xH0SY02LMne;Pc*Km#d!|qDq}K~p zN?Qd*7)7?> zH+B>CUCiCNTWchn_{D{ohJ=C15`rt|uODSUUmEdyWCP{x9&e7`Yxgr-Oj@h;o;5L7 zUO%O!EN|~4HKTlwE=BoC{u{v8yZ;Ve3k53Ime!c+9!LIt_=zEjqC5? z^8Nk!XNYNh$&e8GC+p^c{WW)-Wj=S$GrSLmdwL5etKjW1;h|Gzvg3iWq214?aP7?F zgUHB+P^_4SW_gs$6Xpss?SH?IW5*ROVmkb{p&{ra85tQHBO@C=l#!Ldz`zmu{Ol3C z#O!Pq`9iq^k=b%`a*_AVwY6C-O|>m8Uu5fi+$nZpg^~4o)_#n^U<3w|#~NnJ8|UWd z%Zcx68n(}x6o&m+d3pKzTEq7B|Mgjg#-`7=jr;Y4hJX>p$iH(i;Cu_%Dt8pVE{?#4?iWoT;#|u%5P=Y2kK) zl)Sur&98sqMM4W>O?_MXHPNzSbLIBN#O@t34$s%mAWK58Sy#V5XOR*k>5L8n3xCud z%bc*}uUA4%{jab`MaM2Pxa7Mf_cSV%uW5SU8MioY7_nD^{7;OlAv0$q|A7uDv)~{z zXeEnE>>mkcQRziXfbgz;<#+i#>UZ*&#~k$#33we@=ROi~j|o4!zm&eQ|H-Wk)gamT z`L(6D3lI|@t-ZW#fUzP?Mk7^xMC5$DSefgiy(2%wXF?X=ne+U4$Ox9ImiwCAtTTLx zW1)QOkF4etc~!?CD9hwGhlA3r0T48I+R^^6n1wkQMcAGJ-LRzSknoo9lPVHY%>h@e z>~2i4bl5*SbG!SrTE0Viji=44yr;bSq)`Fk^-{!3eq(KuUfN`J|6*W<{gAumubAjA z(Zp%4vQ0aogMnxK6L0FH>+K=34sBr`FVe4>R9lGhjcth1^F)iM`i%vO-P_|!u4>NG z66Mjt`JSZ#G%dn#HCJd|_nof!r+xhI?Mh~V1w*4{0@sXC4Ck@b@8WH8KVS2H?z$Zu zoe_gPc{<8Ez(B?!~PVzv}m2vOj3!aUQZL7+m-$ z+IR6r;fL2Q2|TB}>Vl`{kMXJu)_wfpoY+Tn=u4EdFkTUKwQUrqat}6E+KP)W?H1Q_ zxgZW+8)LQpSTdJb9wJBu%$S^1@B9gn0Lol8b;a7^EW zo}`?X1+ED_GE|i@Hg*Z^7%=0A5#nXnd%;88Qd6VF9$JhQQWMptq{#L~2}BOV5(Tps zUVBAY&S(~KJ!Y>3!!PW%Oz4mcJ6T#Zb5cEv<)-(G9SL-^m$PpKDi1xq_P5O{mHSw=5BdUjMzmw)Hha3*ua~`Lo9!K`{$KeT z8p;*_S3V|_;ZUmG@S8yg$Xnq_87ZBK&q{}y7~sQk zY||4hZRQ&@y(7L1=E{oie~&0%n=M~o`p;N_{BI&=|M#}-iMaE>gzW_?|4;ef=MCndV=0v7MqCt4w#atNE;3}xdX+6y zqmV)_Wh*pDl%<>X=kxpC$K!ct-sk)?|D4xj2L0)k{nIN!jEZ;u|IbgYZJhop+qIVe zcu~hlM!$(=lAIh(pGvT3-(#&@-1ax(YFvi@H?a}dA+@Q4Ce2q4gc3psVFcdmW%A>{ z64Wm`I5_MTPW@O?ldHvTG9BZ2e2z?cjg;-LO$>%E?hS?nHPJ~|Brd20Av`4+^NtlJZ{>jFD}x4{X?4?|{M+_y6WAuWO=z3&Hj!<_ZK8EH ztYX98*(Is0U(=3GiLFyn`_CyHM@|tce=r(ve8Dz`KOtko0^jkh$v+41jp`XdXKv5? zdC7Bs!0{zjufJM<|3MM!XLoHm&{$GoS@xS{oFT_|BJJD{Ep}%P@u6-4l?y_R!9DM>mV24{|_`m1XbG@zK{)>L6l$6Re#N%v;m=@30b zqzg(Sn`C}BL$6y_{X5%u*B0X?_ddQbBJY4#I4z#MwE4yrID1uK*nKbT1ukA!5B1gF z>31eMz;$Kqi4Xz0zBuuzjb_diD_xgc3TqtU>)D%OO?xQ89Yl)fivBZnSO*Jmv1 zXK#MU1uo_bPEnuk^({KE{!Hy&nf9LNP-c}iSnE_pch`RD*U=bi({g{bHOb?3*7Dn; zuy@tmT}`PUI;21Kmh_Ei4B)MUf6TsaGDf&u#|7uV?+uy)Ii}SUX0^qC89!aHIkM>3 zunZe9{XVURI&2{LR`N~Ow5&~b^!|=&L9Ui;o_C78eKl165goyq_rgo(L^sF9foppO znNoZ2$b!4~t3Cp?NnA5|7v4OLw``G zb4cGTbu1tPXNg>jy+@OtNE zja7J-Tv?iJe?CbLkRpr*^ybXxF?p;R9PI+>7W%yj%MO$pXEF}BXa zWH|SNpOVAfI;U8@OSCT69P-tY@9x#ty;}ZV$L{(vc>Ts62mF-3wNw#I&SLKv>6M zg3VXj=tz#oIJ*3u9yZm&J&mQ#_DKI4D_L03SY);?6U>>w;40B|Lu!6Yg&M^An8d-M zlduC4=ORDlaX!KeH}F%81j9ZFCpd`GaPgd*d(llP`hD3J!TmkiAVcxsSdB8Sqr!Zz zN;rQM_@+U;c0!7&sdDEOX}6xvBYjBDh+KZg`Gl#@YjcXFRoEA6F>mnNhu9nrFDj6HmAdw zx4is^{AG$^#=VX~SDW!kjkx;gEqQ42%* z5Y*ykf-RW!1Y6>q)(eTYObIf8Uf@%6AT~Kkow=ma=Sp(Asx#vb`FoKG-p(<;u&|#` zZUCZmE1@o!rWcBKg;kL6(5c@eTdpHL|DqXpb&!E)Bui(@O$#-05{X2AT%6ZQ;oH^?Q{-RV-7ZB~mgpOsJ4KrtS zwZdbAzP!~E#+r$7b1+hOj7f9hB|HQYFVM!%XD&z_6b4;IRp!M>L3?#Lq%9L<$>RH+ zpMvm_gOmp4Fe4S@lWLB`Q54sxxOsxUsfpb`)g!3K$krcIG+L-R!_+Y)beaR_t%%>O zrLLU=nPi~CN^ZyM8lweYr$dG-yJFoFT@@1>TyKx&T)S1&20ACh7` z8zLVYel3$qo2L9k+2-Sa(?yrC{FTH17%$dvgUud77V{$86bJDJu)zf) zyGR0jS$m)ZLe(PVQ)2Kt3EO>?F=@n8Ze>3$`~Z&3TnumO=10uzlT%t(b6km^}-qYZQ(?zN*h+D|(oelA;=Y0+0uA@>|l`oZg1t&i?a zMyEf1p2GYVbS(thV(i3#= zQ+g4q{tRm_HLhUvSK`h!vvo{rzzcx2F|L_NYz3dRlMcPD?|}3>$sf8ee1s=^eLhpx z7)aC?{t;Tn7^?8Sa+0ovW7rJCdTwMMcmKm&TB801xK~rxiPBqO2}`yHJE@fuGTX`E zCSTqS|Kg+E5?~ib9uR3_;suhZiquOPx}iMwQNdo3@k0r}ad?Azgo&sFf*Qo^;ih+q1_3yxHRO-aJ#zTjt6^jdNUJ! z0ZBh18lZLyYzjUTO6m)9x&zHb!u|*$h7uDeoPgv$R7#=MrJ;I{>0KG*Z0vjP^?b&^ zr+15R-V!R1Ubg=^@p+YIE%=sx_9bb!5rSLDi>;xr;hk9WJIcPU;t$BtPiRY@$pHQH zpvzb6=Sbif4x{B8Z7oKgBO|5)T-|Wd4201YYx1JBXb7@d$xc@EnoGwxp$bi}ir6)!Vu+;m`|(*rzz?VC60+6|^3 zbSH3k5c$z<*sy`fEDsP}vNYFA_r#@CzP&)Cm+KE%`< z2Cz}IO%Mg4b%MmpdH|5zu-s=b^$qENr_h^+@V5G)$8O&ALu5f*B^1D zOb5~YLbj-Abc9eOJ?9>FFq%;ttNQ@&Ogx{={FZv{5%C{29lV>xn@f89ME)sMfu4Lu zep3otRZzZGUadj?)P=mD0gXw`^u4WEQM>wEMstU8C*I%f{E<1^3j}^9vJS?50f{3| z#z=?0*H4o5arnc`g*nRPyw4wGZ7J$6jeix*Sf?v&RkGw^TI?7n!+PfkH`9GLu*?@G zK;#ytrV>GE@e)arh7{B)LpGCVD8QE$e<@LJ9cEXhJy1u*GzyN>RWx5}V}`mPPviE6 zKOp7}lMOTAzNMfwG22#=Oscs6={UT1BA>f7?h1Qd{p*fIc6VLL@U7;fw(u#z`iC=tH#>Dyb>$%wwo8!zr6Qlk1;PArvApMZzVt+%l!- zXkC?FE!IURzhr!GyxxNUYrXrL$%{RHN8H~9KL4QkiS)Mb>;N=82!DkaM?%LbJ13H- zkbTp|v$UhXS{CRgi`eI77bfH9YTyRWN_Y~-3W###?;sxLdcgx4@b>VNE>Z=jgvq|5 z@VfZDy_7xDnX(ADzY0XP4|XWiZB#~8F^{9m8jP?L+*-`kQ-F+KsXmcp)OH3mHyt#G zT&?D<$wB8h?BIlp;*ON3&POiO8r)DL_>%|S;TrCZ-Mq2shu;qr3}R;AR-^*8;ktK; zol)oSlfK13*W#H8+np`?ko_QjN_Bzk90R2*UrI`M^gmJrkM6oC;623B9os_|>80fLD>D%K(CJ}X_o&@BbYjB$2evvL`IEsn zpZ*))zw~^CsriqHtyyn^BZPc+HqsIY6@a+7Q+B~pe9x$RD940ei6Ew80}`}L`{;Ah z6giAdA^reER4M-ue)z}>HNf!LM-tKgZD&M5U{lCHP;02Vig{E2Rqg&;6Egfk3EjF5;V7v58ih8ts{ z2b5lFBF!;*Iu-SMw3SYe&JxPSa-JM~ir1pL&zPO1=PQ8kmDg*C|LRD0U+^|UkDKLN z$rbG<-@RAfN`s|bsoELT?liWVLY1B2oC;^mTn4Uqz6vQhf8Cp`93~ntyqQG=l zBrYq39R|5oQ`Je*8YRae4NY>ZHf*NLI8C{1_zOaAnXsGF9$3=FtP5;074pjq3`2*H zPPqLg<_}lk=G9Gi;(aecZ!p_e(T`LUK-RhSJ{Ue1Ivz&xy7M;@i9l$(VyGxBPBwvF z_V9QL)}ChZm@$;Wbjx1I1#ai>C?q~Ck}LtyGLqVJsHy5qExC{G^b($F^lzaM+GsJa zg)ubuol+OQ?t|VZtg8>RgU>P0}t5iE5kU2bzThoM0a*K6>+ zchC(E|Aat9Jm~4|Bh-d))aWkuDazqK9&lz@g3a{dw{*YMg8iG<{{8{xPKIK z{x&-i@PlOrj{PYzpZ6P4UZSs{r~h@Y*=C(wLEsUbWb{da25QXxM51RomEey|s zA8qW<|FFAZ4{bOiE&{G_)|CV9NVVsw>+gJQZ=z%Vo)l~)C_IEGEG+_;i7dTGiU!(a zEgpbFiEhaYslksR&Wyw?SR%LZiR#lv6oIJEdP^NE(CJFQ8f>dB`h`$qPBVV6wYDA5 zd)w7P?)=^jUjO*7_wHwickuBSSbn5p?Bw@1lZX}mbq2jU_h&xj53r0S{oT7-w2rH9 zHM5|A3HxVGmz_Vk!NA?D{7{?#QTPe*kT_f~sW0_G2Du(w=2yissOkzD zc;#^|O-$Qb7Z^S5VYm!|VJ6(>P^zW0b&0J88E(B`=72DqE?@fPdh066?(x7&%o{84 zRq?}L1{mJ@7;GO3;9)oKfSZx`5y6=3I7l&}=Aljs{66j6V`MzTEBkM5L_WH!kXD4r zmXwtpf8Jh&ThtEG-ChE}8>zQjz#VN5UrS=>J4mgo=>vS`Q(vFcz|0`x|CKNrGmZ*R z5A!3i;G%G-=uQbV zdG9{yV%eknu@=ze;AdqQ75t}a;8B2$1je5b)yhA0SPy!kZ(v02Idjnzo-+5fTDQJ; z4%uUuc@YI2u_|Zn%N=euFh1(xac$W<>;}N&pB5++RC*gEQQN{H^Skh1lRa2jH%tt^ z^L}?X@~8);%Jr7_pJ2Qm!Yqfsj9&Tndjh}xW5>+HpOW)H(Qox7uz3YC{@1_eyg5rK zh6C7GIAZ}3ZX|D)G9P`s+&y0_mp|31V&kopJI*-lSQK1lZVqmTVf z=!gFe%0>_p0t9?aTg^|^oZ(A_`0%a8xcufi4UC%2F{EYDCY_OF~*cmCnVLUsfENdkL?@gkzSc(bIj z6!2NbMIQXA00kK-0+(IGu5x>%dO_0O)DmAD5E~Y=t1FZly8!-Nys2!Al@v!3yXM@XdkK09f%ER{>vhIEF#vcF7 zKoktZs%~pj@s4n+&E3%`kNdz%On5v`Vp%(QJPSW zidR0ZK2q0Ej~O+5YI1mmx8XP609#n}dr+u5r{`dAZ9k;T=o~sf{B0Dz_Ki7l=g00D zB?9)b z$RM$DA_{p2l$B8Wq0>jY)$ER86C~XGfD+@W0Tm9tJ(|0Ip`wSZ^^{l{GD#L4d=xHwi*~YTw56wz^o#$n4)a zq3b*Uao^p|%MU*mkQc5Xo)kykNTOC$sjo8FRrx;(A&P)9o^*JxYLU9S2GDZcMDw$@ zi!S){G!$sadIpX&A(}t2JY<+{om5NBml*Az#39S#!Qcy7=LGQ;N| zt3ZcZoPP5qwApySMX)XVHLQr$ywmA={{cDoX}r&C;O`(7@pacIZ5)@KD4RMy-98Ig z{`#_TW%2hic$>Ln?cs(bp&Uk8)i|1VAZNJxc$|1=_)&jBf=G<0umqO7S4p}~R&PJh z4cZ-?PzJqKR#hX9@{u5F`icEo&rfOU!Eg1?8V#R;P0>a3P^+CbSn|1jcEuNuI^r$P zCYL|Exxm1j2jv=@Py7u?)IUG)a1i|BwgI&#{Ni0?D$4i%dd$6ebWdVt5|~;=!?n{p zGHr5zu{_U$mBR31kf$`QT&A+L8Y0!T)tfgAHX*LB=G%hapj3{Ig!kgzPkXQ1 z)jiYbf!IqA@VP`vGFw{wBS0*pAWJ3pmLhkpci_0c}d4wiiZkB_vES$wAsO+s$?!c6cS=K_-WM`F3~FRHrQ zxDIXgvOLEe*{3=Ec5ZRw(YuBCa|8|wgLOnb@h(Y-EcIRHy8J%{_^u+a^5etusz`5Ar1B|*peQ@{$;e9s;HV5y2g#|~m#}y}P zrVyR!_p|4IjW3{Hi+`6RnE0+W+J-EZP+7(boZxu9!;I_3UHYJ%(Zm^V(^YdQ(u&U}{oH;# z`1wUm$G6UBFC)WlFudr2gJKVBSoG^pja2^AO!zjSvligF)hi>1cLizdSP zNrzKk&dU|qh>J?DkKi6GagVoZrU`*zEqkL2VO$`9ZtlwKdQJn}^ixO(glDdZ%e4JPUA zJzZp=4nfVv#%BE%i1XQ5n}G8y7tmOH5vM#CWmk-T<+OXZr`>gY!pHmOsy`S-;SbIT zQ3!)7BeWv1d)CqLNUZyVr9^5n!j+ctNGbz+mUS%m)f3aF_y85X^lYw_QUS16J*W|* z7rX#f8ecXWw!$CV?ce_BxY3DhbR!=H`m#Qw2L`LZoEmvIhS`1}oAktizcUeYyB27F zK-uN8zsFbG*CC6oA(m=4_61Hjcqb=M;%-!ezfeF`xRHoK;=PiNQqwZHpZu0Ww4#tQ zkb783wN4$P>S=TxCu@G!hOg`XJAK!X_YCsbMBco@@}xET#@33AeZ7ERb@<~HatUw) zlCSRbDE2z)4Yv52_x%sv_-@jxw9)&}fxM?J_;=CCuHF+Cr6XD|wA*V6E>^vo4JB;qs?neYXa1 z_Mrz~#YPLpfg=+QQ%2LDWVbgd5}yuX;pY9$dS(gW3<;GRB5HhgU|!zwu0_{eWd=N`I2(+kTrb4X(KU zn_okNHaS>o6WDNZ&Zoc;?uK1Pe4q9>2;m~2pV*c}^gbaOI7jZFLhS)vrOrd=k9<=@ zuN`BO?ws7M{YXddbh*I^2!CyCY4*heyn6PJO~`rRLX!Pnry>`1SGf6#G1a}_)A>3& z>l1L3B_Ng}5}X&J9EQ^)PDgg%vx}aH^?tCL7?}+6r)4}+$bc)ev~sa0)=x)J_h(C` z)C!cVDy2q>{_F*QtnpQ|Y3o4yrMGh(lunTS!-J1veFdLY248+L9Qil~+kgKtc>_l` zW{^37g{(gZmaG4sT79>UQ*F1#Sn534S2)3lox6BwyJh*y1da>06JZPSAxSr>1({%Z zPB2kXLK!VQtg70mjzb!~#~n4Nwf%IrPDdLGodI)Al+5caVLj_ETQd3kh3gLgobF!Y zbweLtmG`LdI_dq!*UIl}0Dkq>pWu*CARJ7-voER`IU3UvXOi%l`p_i>{+Sl|m^BlP z%O>VM$v;$BU!-61q3ptQe6q@?c8wnOlD{dVMWGF>daaFhytCY-el6TuWM6r}6k99U0FMyp4Xj2SpLRHK;XF5a;Z6Sgf`TI1 zqNt)o&0Zbp_p;}x`^Q1AgMXFr2$fxGw4GF&l?hg|@W3CDNlSrVnSrIg5Fr1v^nZxp-e`arx0o@Kv>G-9SC{Qo~#mIxQ$;=<%Ow~{A`HtTKhO-nP zakuR<=NsIEx(dyU1c~7sk_r37WuD3%QD`_|r1a?!=y+sW&F|P2Df*<4c8(5w@N}(# zF4Sp^o;TC|W^wH-)8@|k-4`C&%Q@lYE+<@HUjeP$zj|K1{>LZeCJ>NB*&7TOg{X%$ zM;J%;-*b+hjYR_L@#*r7@IEUy6;DP_<8oKoBIbvixQN z2{j}RLaoAuNo^LBtEWb?kkBoGHUA02gG|=6^_u4wrj`SL=%*!{ovgC9l$VtseWZB_ zd8+Wn{ROA<&qOTR%V1{nApdx=N|2M5&8fNHNe`;~O(i#qgui*y{HbPr2akWTu^G0Z z{^O|rD2{#>C}$WoUNfM-Z~T=n+ADswF6nisj&_aMMM~km{*31LO2homPK*p)dv$yK ziti$3Yjo})_joUEu-jJmZg@v4F-^+3X5*>wVaSEYRBKaqN}iuL)-PW-n7;Fj#=D0a z>%A)6_;mEzM+@ zTQPp7BCGNP`>Iisyf7JlVMeHvMkd&V?nWzF)yNFt{v*d?ax*OP0G|}+%_i%$BdMt<9F73LJRZ zR9;@bw%XK`C%;ozP=`ZECIX0#h~TiY6cqe#)GvtUQncx+cy2@8sDC-K91;>TJn(mU zxqowYc5`zxNFZ)`*>-F7X5jGuw&rJNpZ#xZ_5KVy2ZyC@Bq9}YiK?rsD=5hE-@Gd? zFE4`2R?SPP-)ZZsLtu!Llhfbjkd4*i=FJz)&CMhFm;e9%e_PGJX4h8#w?(Cf7EJ*QXll8&fR$7zPHbn^+AlNn@NfFVwVdm zHO$L?=dCKDI~x1Y>77FD10d<}ap%YakDV^&67{Xtrhx)hH#Ttg#VuVC z)ok5F&u=YgYx-Mc;N`OM7j<&t_W_@y`PA?`Zk8dBh&M7FzvmPDTz+J1;D@&94(b9sfj=s4%|0-k7-T2ZX;>Dl-?EZSRXJ6V#i0{a?Mv?A&8d9~rabo%3 zJ&tpbtp07C*U1d`k8L`Ct?Ifw_Z2tYjE0L}58v!BFqODYK4ocvld^UGaimmd6(Zu{KEsS8{lg#>~_ z!)4RkA*X;8E}hsoS;TMoZ~4HN+?_-B`3>lA{2fMVAv$hHznP^nXBscRJs@*v2UT5g zS7?0;i>B?A>SnbOf9I{7fGY#sqEb|a46hA?jOO|brIRbKzC7Xu6AU*qZXqL^g9VFm zpM;ixG5w}t!xsS=LR_Z%#)h)J*1twa2Q+AC_hmo1Q)5vX{g&_eXRQYBJZD4YOV7SI z<}uf{YS`o6y!E=4_5bHq-@t$)1?A({x3oF`LGJ4G?8S}cfsKvrzwDO-4%F>qKM=Bg z)YA9ZJ30NgHd{77`@f~_(dqB}e7S{<0tkk2De7~{fLvT$c{v~m6NGH|goK0yqpvW? z=l(0eukBPc{rax}w$u%ODs^ks?C9t1qAdUT^Qmk^YF5+M7P7BptM9|+VENYiF6OEEw-yPbTlcA38@_x1X?W`cVQc;0K|;`P?|mFw bE90Zh8=q&bBcwUF0G2rm0^ytu+3^1WI^7cm diff --git a/samples/content/reader/arena/arena_from_json_ds.vtx b/samples/content/reader/arena/arena_from_json_ds.vtx index af00aae6bc08bafef64c75176f9dc303bb19ab01..6ec23f682802384166d19adb6df90897fed8cbc6 100644 GIT binary patch delta 11899 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 SE?|C>ZULnm9Uut$7RG20q!kb)MLKjK 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 delta 24518 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 diff --git a/sdk/include/vtx/common/readers/frame_reader/flatbuffer_loader.h b/sdk/include/vtx/common/readers/frame_reader/flatbuffer_loader.h index d74187c..a17e7fb 100644 --- a/sdk/include/vtx/common/readers/frame_reader/flatbuffer_loader.h +++ b/sdk/include/vtx/common/readers/frame_reader/flatbuffer_loader.h @@ -1,32 +1,52 @@ /** * @file flatbuffer_loader.h - * @brief Provides an ULTRA-FAST generic loading mechanism from FlatBuffers to VTX native structures using PropertyAddressCache. + * @brief Generic FlatBuffers -> VTX::PropertyContainer loader, on top of GenericLoaderBase. + * + * Inherits LoadField / LoadBlob / AppendActorList / AppendSingleEntity / + * StoreValue / EnsureSize / PushToFlatArray / FillFlatArray from the CRTP base. + * Implements the format-specific bits: ResolveField (cache lookup) and the + * pointer-based Load / LoadStruct / LoadArray that walk FlatBuffer tables. + * + * @author Zenos Interactive */ #pragma once -#include -#include -#include + +#include "vtx/common/readers/frame_reader/loader_base.h" #include "vtx/common/vtx_property_cache.h" #include "vtx/common/vtx_types.h" +#include "vtx/common/vtx_types_helpers.h" + +#include +#include +#include namespace VTX { template struct FlatBufferBinding { - static_assert(sizeof(T) == 0, "ERROR: Missing Bindings for this FlatBuffer type."); + static_assert(sizeof(T) == 0, "ERROR: Missing FlatBufferBinding specialization."); }; - class GenericFlatBufferLoader { - private: - const PropertyAddressCache* cache_; - bool debug_mode_; - + class GenericFlatBufferLoader : public GenericLoaderBase { public: explicit GenericFlatBufferLoader(const PropertyAddressCache& cache, bool debug = false) : cache_(&cache) , debug_mode_(debug) {} + + const PropertyAddress* ResolveField(int32_t entity_type_id, const std::string& /*struct_name*/, + const std::string& field_name) const { + auto struct_it = cache_->structs.find(entity_type_id); + if (struct_it == cache_->structs.end()) + return nullptr; + auto prop_it = struct_it->second.properties.find(field_name); + if (prop_it == struct_it->second.properties.end()) + return nullptr; + return &prop_it->second; + } + + template void Load(const FBType* src, PropertyContainer& dest, const std::string& struct_name) { if (!src) @@ -44,147 +64,56 @@ namespace VTX { } template - void LoadFrame(const FrameFBType* src, VTX::Frame& dest, const std::string& schemaName) { + void LoadFrame(const FrameFBType* src, VTX::Frame& dest, const std::string& schema_name) { if (!src) return; - FlatBufferBinding::TransferToFrame(src, dest, *this, schemaName); + FlatBufferBinding::TransferToFrame(src, dest, *this, schema_name); } - template - void AppendActorList(VTX::Bucket& targetBlock, const std::string& schemaType, const FBVectorType* src_vector, - IdExtractorFunc idExtractor) { + + template + void AppendActorList(VTX::Bucket& bucket, const std::string& schema_type, const FBVectorType* src_vector, + IdFunc id_func) { if (!src_vector || src_vector->size() == 0) return; - targetBlock.entities.reserve(targetBlock.entities.size() + src_vector->size()); - - for (auto it = src_vector->begin(); it != src_vector->end(); ++it) { - ExtractActorWithIdFunc(*it, targetBlock, schemaType, idExtractor); - } + GenericLoaderBase::AppendActorList(bucket, schema_type, *src_vector, id_func); } - template - void AppendSingleEntity(VTX::Bucket& targetBlock, const std::string& schemaType, const FBType* src_item, - IdExtractorFunc idExtractor) { + template + void AppendSingleEntity(VTX::Bucket& bucket, const std::string& schema_type, const FBType* src_item, + IdFunc id_func) { if (!src_item) return; - ExtractActorWithIdFunc(src_item, targetBlock, schemaType, idExtractor); - } - - template - void LoadField(PropertyContainer& dest, const std::string& /*struct_name*/, const std::string& field_name, - const T& value) { - auto struct_it = cache_->structs.find(dest.entity_type_id); - if (struct_it == cache_->structs.end()) - return; - - auto prop_it = struct_it->second.properties.find(field_name); - if (prop_it == struct_it->second.properties.end()) - return; - - const PropertyAddress& addr = prop_it->second; - - switch (addr.type_id) { - case FieldType::String: - StoreValue(dest.string_properties, addr.index, value); - break; - case FieldType::Int8: - case FieldType::Int32: - case FieldType::Enum: - StoreValue(dest.int32_properties, addr.index, value); - break; - case FieldType::Int64: - StoreValue(dest.int64_properties, addr.index, value); - break; - case FieldType::Float: - StoreValue(dest.float_properties, addr.index, value); - break; - case FieldType::Double: - StoreValue(dest.double_properties, addr.index, value); - break; - case FieldType::Bool: - StoreValue(dest.bool_properties, addr.index, value); - break; - case FieldType::Vector: - StoreValue(dest.vector_properties, addr.index, value); - break; - case FieldType::Quat: - StoreValue(dest.quat_properties, addr.index, value); - break; - case FieldType::Transform: - StoreValue(dest.transform_properties, addr.index, value); - break; - case FieldType::FloatRange: - StoreValue(dest.range_properties, addr.index, value); - break; - case FieldType::Struct: - StoreValue(dest.any_struct_properties, addr.index, value); - break; - default: - break; - } + GenericLoaderBase::AppendSingleEntity(bucket, schema_type, src_item, id_func); } - void LoadBlob(PropertyContainer& dest, const std::string& /*struct_name*/, const std::string& field_name, - const void* data, size_t byte_size) { - if (!data || byte_size == 0) - return; - - auto struct_it = cache_->structs.find(dest.entity_type_id); - if (struct_it == cache_->structs.end()) - return; - - auto prop_it = struct_it->second.properties.find(field_name); - if (prop_it == struct_it->second.properties.end()) - return; - - int32_t idx = prop_it->second.index; - const uint8_t* byte_data = static_cast(data); - - for (size_t i = 0; i < byte_size; ++i) { - dest.byte_array_properties.PushBack(idx, byte_data[i]); - } - } template - void LoadStruct(PropertyContainer& dest, const std::string& /*struct_name*/, const std::string& field_name, + void LoadStruct(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, const NestedFBType* src_nested) { if (!src_nested) return; - - auto struct_it = cache_->structs.find(dest.entity_type_id); - if (struct_it == cache_->structs.end()) - return; - - auto prop_it = struct_it->second.properties.find(field_name); - if (prop_it == struct_it->second.properties.end()) + const auto* addr = ResolveField(dest.entity_type_id, struct_name, field_name); + if (!addr) return; - int32_t index = prop_it->second.index; - const std::string& child_schema = prop_it->second.child_type_name; - - EnsureSize(dest.any_struct_properties, index); - Load(src_nested, dest.any_struct_properties[index], child_schema); + this->EnsureSize(dest.any_struct_properties, addr->index); + Load(src_nested, dest.any_struct_properties[addr->index], addr->child_type_name); } template - void LoadArray(PropertyContainer& dest, const std::string& /*struct_name*/, const std::string& field_name, + void LoadArray(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, const FBVectorType* src_array) { if (!src_array || src_array->size() == 0) return; - - auto struct_it = cache_->structs.find(dest.entity_type_id); - if (struct_it == cache_->structs.end()) - return; - - auto prop_it = struct_it->second.properties.find(field_name); - if (prop_it == struct_it->second.properties.end()) + const auto* addr = ResolveField(dest.entity_type_id, struct_name, field_name); + if (!addr) return; - const int32_t idx = prop_it->second.index; - const FieldType type_id = prop_it->second.type_id; - - const std::string& child_schema = prop_it->second.child_type_name; - const VTX::FieldContainerType container = prop_it->second.container_type; + const int32_t idx = addr->index; + const FieldType type_id = addr->type_id; + const std::string& child_schema = addr->child_type_name; + const FieldContainerType container = addr->container_type; using IteratorT = typename FBVectorType::const_iterator; using ElementT = typename std::iterator_traits::value_type; @@ -200,8 +129,8 @@ namespace VTX { Load(item, nested_container, child_schema); if (nested_container.entity_type_id != -1) { - if (container == VTX::FieldContainerType::Map) { - EnsureSize(dest.map_properties, idx); + if (container == FieldContainerType::Map) { + this->EnsureSize(dest.map_properties, idx); std::string map_key; if (!nested_container.string_properties.empty() && @@ -215,7 +144,6 @@ namespace VTX { dest.map_properties[idx].keys.push_back(map_key); dest.map_properties[idx].values.push_back(nested_container); - } else { dest.any_struct_arrays.PushBack(idx, nested_container); } @@ -223,105 +151,17 @@ namespace VTX { } else { PropertyContainer temp; Load(item, temp, child_schema); - PushToFlatArray(dest, type_id, idx, temp); + this->PushToFlatArray(dest, type_id, idx, temp); } } } else { - FillFlatArray(dest, type_id, idx, src_array); + this->FillFlatArray(dest, type_id, idx, *src_array); } } private: - template - void FillFlatArray(PropertyContainer& dest, FieldType type, int32_t idx, const FBVectorT* src) { - if (!src) - return; - for (auto it = src->begin(); it != src->end(); ++it) { - const auto& val = *it; - switch (type) { - case FieldType::Int8: - case FieldType::Int32: - case FieldType::Enum: - dest.int32_arrays.PushBack(idx, static_cast(val)); - break; - case FieldType::Int64: - dest.int64_arrays.PushBack(idx, static_cast(val)); - break; - case FieldType::Float: - dest.float_arrays.PushBack(idx, static_cast(val)); - break; - case FieldType::Double: - dest.double_arrays.PushBack(idx, static_cast(val)); - break; - case FieldType::Bool: - dest.bool_arrays.PushBack(idx, static_cast(val)); - break; - case FieldType::String: - if constexpr (std::is_assignable_v) { - dest.string_arrays.PushBack(idx, val); - } else { - dest.string_arrays.PushBack(idx, std::to_string(val)); - } - break; - default: - break; - } - } - } - - void PushToFlatArray(PropertyContainer& dest, FieldType type, int32_t idx, - const PropertyContainer& temp) const { - switch (type) { - case FieldType::Vector: - if (!temp.vector_properties.empty()) - dest.vector_arrays.PushBack(idx, temp.vector_properties[0]); - break; - case FieldType::Quat: - if (!temp.quat_properties.empty()) - dest.quat_arrays.PushBack(idx, temp.quat_properties[0]); - break; - case FieldType::Transform: - if (!temp.transform_properties.empty()) - dest.transform_arrays.PushBack(idx, temp.transform_properties[0]); - break; - case FieldType::FloatRange: - if (!temp.range_properties.empty()) - dest.range_arrays.PushBack(idx, temp.range_properties[0]); - break; - default: - break; - } - } - - template - void ExtractActorWithIdFunc(const ActorPtrT src, VTX::Bucket& block, const std::string& schemaType, - IdExtractorFunc idExtractor) { - PropertyContainer& entity = block.entities.emplace_back(); - Load(src, entity, schemaType); - std::string uid = idExtractor(src); - block.unique_ids.push_back(uid); - } - - template - inline void EnsureSize(Vec& v, size_t index) { - if (v.size() <= index) { - v.resize(index + 1); - } - } - - template - inline void StoreValue(Vec& vector, size_t index, const V& val) { - EnsureSize(vector, index); - - if constexpr (std::is_same_v) { - if constexpr (std::is_convertible_v) { - vector[index] = val; - } else if constexpr (std::is_arithmetic_v) { - vector[index] = std::to_string(val); - } - } else if constexpr (std::is_assignable_v) { - vector[index] = static_cast(val); - } - } + const PropertyAddressCache* cache_; + bool debug_mode_; }; -} // namespace VTX \ No newline at end of file + +} // namespace VTX diff --git a/sdk/include/vtx/common/readers/frame_reader/protobuff_loader.h b/sdk/include/vtx/common/readers/frame_reader/protobuff_loader.h index f7c0d23..85083ac 100644 --- a/sdk/include/vtx/common/readers/frame_reader/protobuff_loader.h +++ b/sdk/include/vtx/common/readers/frame_reader/protobuff_loader.h @@ -1,435 +1,116 @@ /** - * @file protobuffer_loader.h - * @brief Provides a generic loading mechanism from Protobuf messages to VTX native structures using a SchemaRegistry. + * @file protobuff_loader.h + * @brief Generic Protobuf -> VTX::PropertyContainer loader, on top of GenericLoaderBase. + * + * Inherits LoadField / LoadBlob / AppendActorList / AppendSingleEntity / + * StoreValue / EnsureSize / PushToFlatArray / FillFlatArray from the CRTP base. + * Implements the format-specific bits: ResolveField (cache lookup) and the + * reference-based Load / LoadStruct / LoadArray that walk Protobuf messages. + * + * Constructor still accepts a SchemaRegistry& for source-compat with existing + * callers, but only the PropertyAddressCache is retained internally. + * * @author Zenos Interactive */ #pragma once -#include -#include + +#include "vtx/common/readers/frame_reader/loader_base.h" +#include "vtx/common/readers/schema_reader/schema_registry.h" +#include "vtx/common/vtx_property_cache.h" #include "vtx/common/vtx_types.h" #include "vtx/common/vtx_types_helpers.h" +#include +#include +#include + namespace VTX { - // Forward declaration for the binding logic used by the loader template struct ProtoBinding; - /** - * @class GenericProtobufLoader - * @brief Orchestrates the transfer of data from Protobuf objects to VTX PropertyContainers. - * * This class uses a SchemaRegistry to map Protobuf fields to specific indices and types - * in the native VTX data structures, supporting nested structs, arrays, and primitive types. - */ - class GenericProtobufLoader { - private: - const SchemaRegistry* schema_; ///< Reference to the schema mapping for type_max_indices/field resolution. - bool debug_mode_; ///< If true, logs warnings and loading steps to stdout. - + class GenericProtobufLoader : public GenericLoaderBase { public: - /** - * @brief Constructs a new Generic Protobuf Loader. - * @param schema The registry containing field and type metadata. - * @param debug Enable or disable debug logging. - */ explicit GenericProtobufLoader(const SchemaRegistry& schema, bool debug = false) - : schema_(&schema) + : cache_(&schema.GetPropertyCache()) , debug_mode_(debug) {} - /** - * @brief Loads data from a Protobuf message into a native PropertyContainer. - * @tparam ProtoType The Protobuf message type generated by protoc. - * @param src The source Protobuf message. - * @param dest The target PropertyContainer to populate. - * @param struct_name The name of the structure in the SchemaRegistry. - */ + const PropertyAddress* ResolveField(int32_t entity_type_id, const std::string& /*struct_name*/, + const std::string& field_name) const { + auto struct_it = cache_->structs.find(entity_type_id); + if (struct_it == cache_->structs.end()) + return nullptr; + auto prop_it = struct_it->second.properties.find(field_name); + if (prop_it == struct_it->second.properties.end()) + return nullptr; + return &prop_it->second; + } + + template void Load(const ProtoType& src, PropertyContainer& dest, const std::string& struct_name) { - if (debug_mode_) { - std::cout << "[LOADER] Loading Struct: " << struct_name << "\n"; + if (dest.entity_type_id == -1) { + auto it = cache_->name_to_id.find(struct_name); + if (it != cache_->name_to_id.end()) { + dest.entity_type_id = it->second; + } } - dest.entity_type_id = schema_->GetStructTypeId(struct_name); ProtoBinding::Transfer(src, dest, *this, struct_name); dest.content_hash = Helpers::CalculateContainerHash(dest); } - /** - * @brief Loads a high-level Frame Protobuf message into a VTX::Frame. - * @tparam FrameProtoType The Protobuf type representing a full data frame. - * @param src The source Frame Protobuf message. - * @param dest The target VTX::Frame object. - * @param schemaName The registry name for the frame schema. - */ template - void LoadFrame(const FrameProtoType& src, VTX::Frame& dest, const std::string& schemaName) { - ProtoBinding::TransferToFrame(src, dest, *this, schemaName); + void LoadFrame(const FrameProtoType& src, VTX::Frame& dest, const std::string& schema_name) { + ProtoBinding::TransferToFrame(src, dest, *this, schema_name); } - /** - * @brief Appends a list of actors/entities from a repeated Protobuf field to a Data block. - * @tparam RepeatedProtoType A Protobuf repeated field or container. - * @tparam IdExtractorFunc A functor or lambda to extract a unique ID string from the ProtoType. - * @param targetBlock The VTX::Data block where entities will be added. - * @param schemaType The schema type name for the entities. - * @param src_array The source container of Protobuf messages. - * @param idExtractor Instance of the ID extractor function. - */ - template - void AppendActorList(VTX::Bucket& targetBlock, const std::string& schemaType, - const RepeatedProtoType& src_array, IdExtractorFunc idExtractor) { - if (src_array.empty()) - return; - - targetBlock.entities.reserve(targetBlock.entities.size() + src_array.size()); - - for (const auto& item : src_array) { - ExtractActorWithIdFunc(item, targetBlock, schemaType, idExtractor); - } - } - - /** - * @brief Appends a single actor/entity to a Data block. - * @tparam ProtoType The Protobuf message type. - * @tparam IdExtractorFunc Functor to extract the unique ID. - * @param targetBlock Target data block. - * @param schemaType Schema type name. - * @param src_item The source Protobuf message. - * @param idExtractor The ID extractor function. - */ - template - void AppendSingleActor(VTX::Bucket& targetBlock, const std::string& schemaType, const ProtoType& src_item, - IdExtractorFunc idExtractor) { - ExtractActorWithIdFunc(src_item, targetBlock, schemaType, idExtractor); - } - - /** - * @brief Sets a single field value in a PropertyContainer based on schema metadata. - * @tparam T The native C++ type of the value. - * @param dest Target PropertyContainer. - * @param struct_name Name of the parent structure in schema. - * @param field_name Name of the field to set. - * @param value The value to assign. - * @note Performs type conversion and bounds checking via EnsureSize. - */ - template - void LoadField(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, - const T& value) { - const auto* field_info = schema_->GetField(struct_name, field_name); - - if (!field_info) { - if (debug_mode_) - std::cout << " [WARNING] Field not found: " << struct_name << "::" << field_name << "\n"; - return; - } - - int32_t idx = field_info->index; - VTX::FieldType target_type = field_info->type_id; - - if (debug_mode_) { - std::cout << " [SET] " << struct_name << "::" << field_name - << " (EnumID: " << static_cast(target_type) << ")" - << " -> Index: " << idx << "\n"; - } - switch (target_type) { - case FieldType::String: - StoreValue(dest.string_properties, idx, value); - break; - case FieldType::Int8: - case FieldType::Int32: - case FieldType::Enum: - StoreValue(dest.int32_properties, idx, value); - break; - case FieldType::Int64: - StoreValue(dest.int64_properties, idx, value); - break; - case FieldType::Float: - StoreValue(dest.float_properties, idx, value); - break; - case FieldType::Double: - StoreValue(dest.double_properties, idx, value); - break; - case FieldType::Bool: - StoreValue(dest.bool_properties, idx, value); - break; - case FieldType::Vector: - StoreValue(dest.vector_properties, idx, value); - break; - case FieldType::Quat: - StoreValue(dest.quat_properties, idx, value); - break; - case FieldType::Transform: - StoreValue(dest.transform_properties, idx, value); - break; - case FieldType::FloatRange: - StoreValue(dest.range_properties, idx, value); - break; - case FieldType::Struct: - StoreValue(dest.any_struct_properties, idx, value); - break; - case FieldType::None: - default: - break; - } - } - - /** - * @brief Handles the loading of a nested Protobuf message into a recursive PropertyContainer. - * @tparam NestedProtoType The type of the nested Protobuf message. - * @param dest The parent PropertyContainer. - * @param struct_name Name of the parent structure. - * @param field_name Name of the field containing the nested struct. - * @param src_nested The source nested Protobuf message. - */ template void LoadStruct(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, const NestedProtoType& src_nested) { - const auto* field_info = schema_->GetField(struct_name, field_name); - if (!field_info) + const auto* addr = ResolveField(dest.entity_type_id, struct_name, field_name); + if (!addr) return; - int32_t index = field_info->index; - std::string child_type_name = field_info->struct_type; - - if (debug_mode_) { - std::cout << " [STRUCT] " << struct_name << "::" << field_name << " -> Index: " << index - << " (SchemaType: " << child_type_name << ")" - << "\n"; - } - - EnsureSize(dest.any_struct_properties, index); - Load(src_nested, dest.any_struct_properties[index], child_type_name); - } - - /** - * @brief Fills a flattened SoA (Structure of Arrays) from a repeated Protobuf primitive field. - * @details Iterates through the source collection and uses the PushBack method of FlatArray - * to maintain the data and offset integrity within the PropertyContainer. - * @tparam RepeatedT A Protobuf repeated field or compatible container. - * @param dest The target PropertyContainer holding the SoA structures. - * @param type The VTX FieldType identifying the specific flat array to target. - * @param idx The index of the logical sub-array within the FlatArray. - * @param src The source collection of primitive values. - */ - template - void FillFlatArray(PropertyContainer& dest, FieldType type, int32_t idx, const RepeatedT& src) { - for (const auto& val : src) { - // Compile-time check: is the value a string-like type? - constexpr bool is_string_type = std::is_convertible_v || - std::is_same_v, std::string>; - - switch (type) { - case FieldType::Int8: - case FieldType::Int32: - case FieldType::Enum: - if constexpr (!is_string_type) { - dest.int32_arrays.PushBack(idx, static_cast(val)); - } else { - if (debug_mode_) - std::cerr << "[LOADER] Error: Trying to cast a string to Int32 array.\n"; - } - break; - - case FieldType::Int64: - if constexpr (!is_string_type) { - dest.int64_arrays.PushBack(idx, static_cast(val)); - } else { - if (debug_mode_) - std::cerr << "[LOADER] Error: Trying to cast a string to Int64 array.\n"; - } - break; - - case FieldType::Float: - if constexpr (!is_string_type) { - dest.float_arrays.PushBack(idx, static_cast(val)); - } else { - if (debug_mode_) - std::cerr << "[LOADER] Error: Trying to cast a string to Float array.\n"; - } - break; - - case FieldType::Double: - if constexpr (!is_string_type) { - dest.double_arrays.PushBack(idx, static_cast(val)); - } else { - if (debug_mode_) - std::cerr << "[LOADER] Error: Trying to cast a string to Double array.\n"; - } - break; - - case FieldType::Bool: - if constexpr (!is_string_type) { - dest.bool_arrays.PushBack(idx, static_cast(val)); - } else { - if (debug_mode_) - std::cerr << "[LOADER] Error: Trying to cast a string to Bool array.\n"; - } - break; - - case FieldType::String: - if constexpr (is_string_type) { - dest.string_arrays.PushBack(idx, std::string(val)); - } else if constexpr (std::is_arithmetic_v>) { - dest.string_arrays.PushBack(idx, std::to_string(val)); - } - break; - - default: - if (debug_mode_) { - std::cerr << "[LOADER] FillFlatArray: FieldType " << static_cast(type) - << " is not a supported primitive for flat arrays.\n"; - } - break; - } - } - } - - /** - * @brief Helper to push a complex math object that was temporarily loaded into its SoA FlatArray. - * @details This is used when an array contains complex types (Vector, Quat, etc.) that the - * loader first processes into a single PropertyContainer before flattening them into the - * main SoA structure. - * * @param dest The target PropertyContainer holding the SoA arrays. - * @param type The VTX FieldType identifying which array to target. - * @param idx The specific sub-array index within the FlatArray. - * @param temp The temporary container where the single object was initially loaded. - */ - void PushToFlatArray(PropertyContainer& dest, FieldType type, int32_t idx, - const PropertyContainer& temp) const { - switch (type) { - case FieldType::Vector: - if (!temp.vector_properties.empty()) { - dest.vector_arrays.PushBack(idx, temp.vector_properties[0]); - } - break; - - case FieldType::Quat: - if (!temp.quat_properties.empty()) { - dest.quat_arrays.PushBack(idx, temp.quat_properties[0]); - } - break; - - case FieldType::Transform: - if (!temp.transform_properties.empty()) { - dest.transform_arrays.PushBack(idx, temp.transform_properties[0]); - } - break; - - case FieldType::FloatRange: - if (!temp.range_properties.empty()) { - dest.range_arrays.PushBack(idx, temp.range_properties[0]); - } - break; - - case FieldType::Struct: - if (temp.entity_type_id != -1) { - dest.any_struct_arrays.PushBack(idx, temp); - } - break; - - default: - if (debug_mode_) { - std::cerr << "[LOADER] PushToFlatArray: Type " << static_cast(type) - << " is not a complex math type_max_indices supported by SoA flattening.\n"; - } - break; - } + this->EnsureSize(dest.any_struct_properties, addr->index); + Load(src_nested, dest.any_struct_properties[addr->index], addr->child_type_name); } - /** - * @brief Serializes a repeated Protobuf field (array) into the appropriate VTX property array. - * @tparam RepeatedProtoType A Protobuf container of elements. - * @param dest Target PropertyContainer. - * @param struct_name Parent structure name. - * @param field_name Field name. - * @param src_array The source collection of values/messages. - */ template void LoadArray(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, const RepeatedProtoType& src_array) { - const auto* field_info = schema_->GetField(struct_name, field_name); - if (!field_info || src_array.empty()) + if (src_array.empty()) + return; + const auto* addr = ResolveField(dest.entity_type_id, struct_name, field_name); + if (!addr) return; using ElementT = typename RepeatedProtoType::value_type; - const int32_t idx = field_info->index; + const int32_t idx = addr->index; + const FieldType type_id = addr->type_id; if constexpr (std::is_base_of_v) { - const std::string& child_schema = field_info->struct_type; - + const std::string& child_schema = addr->child_type_name; for (const auto& item : src_array) { - if (field_info->type_id == FieldType::Struct) { + if (type_id == FieldType::Struct) { PropertyContainer child_container; Load(item, child_container, child_schema); dest.any_struct_arrays.PushBack(idx, child_container); } else { PropertyContainer temp; Load(item, temp, child_schema); - PushToFlatArray(dest, field_info->type_id, idx, temp); + this->PushToFlatArray(dest, type_id, idx, temp); } } } else { - FillFlatArray(dest, field_info->type_id, idx, src_array); - } - } - - /** - * @brief Injects a memory block (Blob) directly into byte_array_properties - */ - void LoadBlob(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, - const void* data, size_t byte_size) { - const auto* field_info = schema_->GetField(struct_name, field_name); - if (!field_info || byte_size == 0) - return; - - const uint8_t* byte_data = static_cast(data); - - for (size_t i = 0; i < byte_size; ++i) { - dest.byte_array_properties.PushBack(field_info->index, byte_data[i]); + this->FillFlatArray(dest, type_id, idx, src_array); } } private: - /** - * @brief Internal helper to load an actor and extract its ID. - * @tparam ActorT Protobuf message type of the actor. - * @tparam IdExtractorFunc ID extraction logic. - * @param src Source actor message. - * @param block Target data block. - * @param schemaType Entity schema type. - * @param idExtractor Function instance. - */ - template - void ExtractActorWithIdFunc(const ActorT& src, VTX::Bucket& block, const std::string& schemaType, - IdExtractorFunc idExtractor) { - PropertyContainer& entity = block.entities.emplace_back(); - Load(src, entity, schemaType); - Helpers::CalculateContainerHash(entity); - std::string uid = idExtractor(src); - block.unique_ids.push_back(uid); - } - - /** - * @brief Internal utility to resize a vector to accommodate a specific index. - * @tparam Vec Vector type. - * @param v Vector reference. - * @param index Desired index. - */ - template - void EnsureSize(Vec& v, size_t index) { - if (v.size() <= index) { - v.resize(index + 1); - } - } - - template - void StoreValue(Vec& vector, size_t index, const V& val) { - EnsureSize(vector, index); - if constexpr (std::is_same_v && std::is_arithmetic_v) - vector[index] = std::to_string(val); - else if constexpr (std::is_assignable_v) - vector[index] = static_cast(val); - } + const PropertyAddressCache* cache_; + bool debug_mode_; }; + } // namespace VTX From 398147316be10af9a0a1eb5d8ae180fc039bc213 Mon Sep 17 00:00:00 2001 From: Alejandro Canela Date: Thu, 14 May 2026 15:19:45 +0200 Subject: [PATCH 3/4] feat(common): ADL conversion hook for custom client types --- .../common/readers/frame_reader/loader_base.h | 41 +++- tests/CMakeLists.txt | 1 + tests/common/test_native_loader.cpp | 215 ++++++++++++++++++ 3 files changed, 254 insertions(+), 3 deletions(-) create mode 100644 tests/common/test_native_loader.cpp diff --git a/sdk/include/vtx/common/readers/frame_reader/loader_base.h b/sdk/include/vtx/common/readers/frame_reader/loader_base.h index 405806a..85727da 100644 --- a/sdk/include/vtx/common/readers/frame_reader/loader_base.h +++ b/sdk/include/vtx/common/readers/frame_reader/loader_base.h @@ -7,13 +7,41 @@ #include "vtx/common/vtx_property_cache.h" #include "vtx/common/vtx_types.h" +#include +#include +#include #include -#include #include #include -#include -#include +#include + namespace VTX { + + /** + * @brief ADL conversion hook for custom client types. + * + * Clients opt in to automatic conversion by defining a free function + * `to_vtx_value` in the SAME namespace as their type, returning a + * VTX-native or std type that LoadField already understands: + * + * namespace mygame { + * struct FVector { float X, Y, Z; }; + * inline VTX::Vector to_vtx_value(const FVector& v) { return {v.X, v.Y, v.Z}; } + * } + * + * Then a StructMapping that maps `&MyStruct::position` (where + * position is FVector) "just works": GenericLoaderBase::LoadField applies + * the ADL-resolved conversion automatically before storing. + * + * @note Do NOT define to_vtx_value for VTX-native types (Vector, Quat, + * Transform, FloatRange, PropertyContainer) -- that would cause + * infinite recursion in LoadField. + */ + template + concept HasVtxConvert = requires(const T& t) { + { to_vtx_value(t) }; + }; + /** * @brief CRTP base for the "source-format -> PropertyContainer" loader family. * @@ -39,6 +67,13 @@ namespace VTX { template void LoadField(PropertyContainer& dest, const std::string& struct_name, const std::string& field_name, const T& value) { + // ADL hook: if T has a `to_vtx_value` overload reachable via ADL, + // apply it first and recurse with the converted (VTX-native) value. + if constexpr (HasVtxConvert) { + this->LoadField(dest, struct_name, field_name, to_vtx_value(value)); + return; + } + const PropertyAddress* address = AsDerived().ResolveField(dest.entity_type_id, struct_name, field_name); if (!address) { return; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ad40cce..5587114 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -63,6 +63,7 @@ add_executable(vtx_tests common/test_time_utils.cpp common/test_frame_accessor.cpp common/test_vtx_game_times_extended.cpp + common/test_native_loader.cpp writer/test_writer_basic.cpp writer/test_writer_edges.cpp diff --git a/tests/common/test_native_loader.cpp b/tests/common/test_native_loader.cpp new file mode 100644 index 0000000..94aeb49 --- /dev/null +++ b/tests/common/test_native_loader.cpp @@ -0,0 +1,215 @@ +// Tests for VTX::GenericNativeLoader + VTX::StructMapping and the +// ADL `to_vtx_value` hook (declared in loader_base.h). +// +// Coverage: +// - Load() walks StructMapping::GetFields() and lands every member +// in the correct PropertyContainer slot. +// - The ADL hook converts a client's custom math type into VTX::Vector +// transparently inside LoadField, without StructMapping<> ever knowing +// about the conversion. + +#include + +#include "vtx/common/adapters/native/struct_mapping.h" +#include "vtx/common/readers/frame_reader/native_loader.h" +#include "vtx/common/readers/schema_reader/schema_registry.h" +#include "vtx/common/vtx_property_cache.h" +#include "vtx/common/vtx_types.h" + +#include "util/test_fixtures.h" + +#include + +namespace vtx_native_loader_test { + + // ----------------------------------------------------------------- + // Setup 1: a player struct that already uses VTX::Vector / VTX::Quat + // ----------------------------------------------------------------- + + struct PlainPlayer { + std::string unique_id; + std::string name; + int team = 0; + float health = 100.0f; + float armor = 50.0f; + VTX::Vector position; + VTX::Quat rotation; + VTX::Vector velocity; + bool is_alive = true; + int score = 0; + int deaths = 0; + }; + + // ----------------------------------------------------------------- + // Setup 2: a player struct that uses a CUSTOM math type which the + // client converts to VTX::Vector via the ADL hook. + // ----------------------------------------------------------------- + + struct CustomVec3 { + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + }; + + // ADL hook -- defined in the SAME namespace as the custom type so that + // unqualified `to_vtx_value(v)` inside GenericLoaderBase::LoadField + // (which lives in namespace VTX) finds it via argument-dependent lookup. + inline VTX::Vector to_vtx_value(const CustomVec3& v) { + return {static_cast(v.x), static_cast(v.y), static_cast(v.z)}; + } + + struct PlayerWithCustomVec { + std::string unique_id; + std::string name; + int team = 0; + float health = 100.0f; + float armor = 50.0f; + CustomVec3 position; // <-- custom type + VTX::Quat rotation; + CustomVec3 velocity; // <-- custom type + bool is_alive = true; + int score = 0; + int deaths = 0; + }; + +} // namespace vtx_native_loader_test + +// StructMapping specializations live in namespace VTX (qualified form). + +template <> +struct VTX::StructMapping { + static constexpr auto GetFields() { + using P = vtx_native_loader_test::PlainPlayer; + return std::make_tuple(MakeStructField("UniqueID", &P::unique_id), MakeStructField("Name", &P::name), + MakeStructField("Team", &P::team), MakeStructField("Health", &P::health), + MakeStructField("Armor", &P::armor), MakeStructField("Position", &P::position), + MakeStructField("Rotation", &P::rotation), MakeStructField("Velocity", &P::velocity), + MakeStructField("IsAlive", &P::is_alive), MakeStructField("Score", &P::score), + MakeStructField("Deaths", &P::deaths)); + } +}; + +template <> +struct VTX::StructMapping { + static constexpr auto GetFields() { + using P = vtx_native_loader_test::PlayerWithCustomVec; + return std::make_tuple(MakeStructField("UniqueID", &P::unique_id), MakeStructField("Name", &P::name), + MakeStructField("Team", &P::team), MakeStructField("Health", &P::health), + MakeStructField("Armor", &P::armor), MakeStructField("Position", &P::position), + MakeStructField("Rotation", &P::rotation), MakeStructField("Velocity", &P::velocity), + MakeStructField("IsAlive", &P::is_alive), MakeStructField("Score", &P::score), + MakeStructField("Deaths", &P::deaths)); + } +}; + +namespace { + std::string SchemaPath() { + return VtxTest::FixturePath("test_schema.json"); + } +} // namespace + +// =================================================================== +// Tests +// =================================================================== + +TEST(NativeLoader, LoadsAllSlotsFromMappedStruct) { + VTX::SchemaRegistry schema; + ASSERT_TRUE(schema.LoadFromJson(SchemaPath())); + + VTX::GenericNativeLoader loader(schema.GetPropertyCache()); + + vtx_native_loader_test::PlainPlayer p {}; + p.unique_id = "player_42"; + p.name = "Alice"; + p.team = 1; + p.health = 87.5f; + p.armor = 30.0f; + p.position = {1.0, 2.0, 3.0}; + p.rotation = {0.0f, 0.0f, 0.0f, 1.0f}; + p.velocity = {-0.5, 0.0, 0.5}; + p.is_alive = true; + p.score = 5; + p.deaths = 2; + + VTX::PropertyContainer dest; + loader.Load(p, dest, "Player"); + + EXPECT_GE(dest.entity_type_id, 0); + + ASSERT_EQ(dest.string_properties.size(), 2u); + EXPECT_EQ(dest.string_properties[0], "player_42"); + EXPECT_EQ(dest.string_properties[1], "Alice"); + + ASSERT_EQ(dest.int32_properties.size(), 3u); + EXPECT_EQ(dest.int32_properties[0], 1); // Team + EXPECT_EQ(dest.int32_properties[1], 5); // Score + EXPECT_EQ(dest.int32_properties[2], 2); // Deaths + + ASSERT_EQ(dest.float_properties.size(), 2u); + EXPECT_FLOAT_EQ(dest.float_properties[0], 87.5f); + EXPECT_FLOAT_EQ(dest.float_properties[1], 30.0f); + + ASSERT_EQ(dest.vector_properties.size(), 2u); + EXPECT_DOUBLE_EQ(dest.vector_properties[0].x, 1.0); + EXPECT_DOUBLE_EQ(dest.vector_properties[0].y, 2.0); + EXPECT_DOUBLE_EQ(dest.vector_properties[0].z, 3.0); + EXPECT_DOUBLE_EQ(dest.vector_properties[1].x, -0.5); + EXPECT_DOUBLE_EQ(dest.vector_properties[1].z, 0.5); + + ASSERT_EQ(dest.quat_properties.size(), 1u); + EXPECT_FLOAT_EQ(dest.quat_properties[0].w, 1.0f); + + ASSERT_EQ(dest.bool_properties.size(), 1u); + EXPECT_TRUE(dest.bool_properties[0]); + + EXPECT_NE(dest.content_hash, 0u); +} + +TEST(NativeLoader, ADLConversionFromCustomMathType) { + VTX::SchemaRegistry schema; + ASSERT_TRUE(schema.LoadFromJson(SchemaPath())); + + VTX::GenericNativeLoader loader(schema.GetPropertyCache()); + + vtx_native_loader_test::PlayerWithCustomVec p {}; + p.unique_id = "custom_player"; + p.team = 2; + p.health = 50.0f; + p.position = {1.5f, 2.5f, 3.5f}; // CustomVec3 -> ADL -> VTX::Vector + p.velocity = {-1.0f, 0.0f, 2.0f}; // CustomVec3 -> ADL -> VTX::Vector + + VTX::PropertyContainer dest; + loader.Load(p, dest, "Player"); + + ASSERT_EQ(dest.vector_properties.size(), 2u); + + // Position: client's CustomVec3 was converted to VTX::Vector via ADL. + EXPECT_DOUBLE_EQ(dest.vector_properties[0].x, 1.5); + EXPECT_DOUBLE_EQ(dest.vector_properties[0].y, 2.5); + EXPECT_DOUBLE_EQ(dest.vector_properties[0].z, 3.5); + + // Velocity: same path, same result. + EXPECT_DOUBLE_EQ(dest.vector_properties[1].x, -1.0); + EXPECT_DOUBLE_EQ(dest.vector_properties[1].y, 0.0); + EXPECT_DOUBLE_EQ(dest.vector_properties[1].z, 2.0); + + // Non-ADL slots remain unaffected. + EXPECT_EQ(dest.int32_properties[0], 2); + EXPECT_FLOAT_EQ(dest.float_properties[0], 50.0f); +} + +TEST(NativeLoader, HasVtxConvertConceptDetectsAdlOverload) { + // The concept must see the user's hook via ADL. + static_assert(VTX::HasVtxConvert, + "ADL must find to_vtx_value(CustomVec3) in its declaring namespace."); + + // Built-in / VTX-native types must NOT satisfy the concept (nobody defined + // to_vtx_value for them, so LoadField goes through the normal switch). + static_assert(!VTX::HasVtxConvert); + static_assert(!VTX::HasVtxConvert); + static_assert(!VTX::HasVtxConvert); + static_assert(!VTX::HasVtxConvert); + static_assert(!VTX::HasVtxConvert); + + SUCCEED(); +} From 5d3f59b6468214066c8998ae1dbc8306a5ca7955 Mon Sep 17 00:00:00 2001 From: Alejandro Canela Date: Thu, 14 May 2026 16:00:19 +0200 Subject: [PATCH 4/4] fix: fix clang format --- sdk/include/vtx/common/readers/frame_reader/loader_base.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sdk/include/vtx/common/readers/frame_reader/loader_base.h b/sdk/include/vtx/common/readers/frame_reader/loader_base.h index 85727da..6d8008f 100644 --- a/sdk/include/vtx/common/readers/frame_reader/loader_base.h +++ b/sdk/include/vtx/common/readers/frame_reader/loader_base.h @@ -38,9 +38,7 @@ namespace VTX { * infinite recursion in LoadField. */ template - concept HasVtxConvert = requires(const T& t) { - { to_vtx_value(t) }; - }; + concept HasVtxConvert = requires(const T& t) { to_vtx_value(t); }; /** * @brief CRTP base for the "source-format -> PropertyContainer" loader family.