From c01e00559b6196fa386d23a206f5ea6fdc7eff63 Mon Sep 17 00:00:00 2001 From: StillHammer Date: Tue, 28 Oct 2025 12:38:11 +0800 Subject: [PATCH] Refactor: Replace JSON with IDataNode abstraction in all interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major architectural improvement to decouple interfaces from JSON implementation: **New Abstractions:** - Created IDataValue interface for type-safe data access - All interfaces now use IDataNode instead of nlohmann::json - Enables future backend flexibility (JSON, MessagePack, etc.) **Updated Interfaces:** - ISerializable: serialize() returns IDataNode, deserialize() takes IDataNode - IModule: process(), getState(), setState(), getHealthStatus() use IDataNode - IIO: Message struct and publish() use IDataNode - ITaskScheduler: scheduleTask() and getCompletedTask() use IDataNode - IModuleSystem: queryModule() uses IDataNode - IEngine: Removed JSON dependency - IDataNode: getData(), setData(), queryByProperty() use IDataValue **Benefits:** - Clean separation between interface and implementation - No JSON leakage into public APIs - Easier testing and mocking - Potential for multiple backend implementations - Better encapsulation and abstraction **Note:** Concrete implementations still use JSON internally - this is an interface-only refactoring for better architecture. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CMakeLists.txt | 5 ++++ include/grove/ASerializable.h | 9 ++++--- include/grove/IDataNode.h | 20 +++++++--------- include/grove/IDataValue.h | 43 ++++++++++++++++++++++++++++++++++ include/grove/IEngine.h | 4 +--- include/grove/IIO.h | 11 ++++----- include/grove/IModule.h | 24 +++++++++---------- include/grove/IModuleSystem.h | 9 +++---- include/grove/ISerializable.h | 9 ++++--- include/grove/ITaskScheduler.h | 19 ++++++++------- src/DebugEngine.cpp | 2 +- src/EngineFactory.cpp | 2 +- src/IOFactory.cpp | 6 ++--- src/ImGuiUI.cpp | 2 +- src/IntraIO.cpp | 4 ++-- src/IntraIOManager.cpp | 4 ++-- src/ModuleFactory.cpp | 2 +- src/ModuleSystemFactory.cpp | 4 ++-- src/ResourceRegistry.cpp | 2 +- src/SequentialModuleSystem.cpp | 2 +- 20 files changed, 110 insertions(+), 73 deletions(-) create mode 100644 include/grove/IDataValue.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fadce18..507def3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,11 @@ if(GROVE_BUILD_IMPLEMENTATIONS) GroveEngine::core ) + # If imgui is available from parent project, link it + if(TARGET imgui_backends) + target_link_libraries(grove_impl PUBLIC imgui_backends) + endif() + add_library(GroveEngine::impl ALIAS grove_impl) endif() diff --git a/include/grove/ASerializable.h b/include/grove/ASerializable.h index c531e40..5e6ca86 100644 --- a/include/grove/ASerializable.h +++ b/include/grove/ASerializable.h @@ -1,9 +1,8 @@ #pragma once -#include +#include "IDataNode.h" #include - -using json = nlohmann::json; +#include namespace warfactory { @@ -19,8 +18,8 @@ public: const std::string& getInstanceId() const { return instance_id; } - virtual json serialize() const = 0; - virtual void deserialize(const json& data) = 0; + virtual std::unique_ptr serialize() const = 0; + virtual void deserialize(const IDataNode& data) = 0; protected: void registerForSerialization(); diff --git a/include/grove/IDataNode.h b/include/grove/IDataNode.h index 1f1df78..2f42733 100644 --- a/include/grove/IDataNode.h +++ b/include/grove/IDataNode.h @@ -4,18 +4,16 @@ #include #include #include -#include +#include "IDataValue.h" namespace warfactory { -using json = nlohmann::json; - /** * @brief Interface for a single node in the data tree * * Each node can have: * - Children nodes (tree navigation) - * - Its own data blob (JSON) + * - Its own data blob (IDataValue) * - Properties accessible by name with type safety */ class IDataNode { @@ -112,12 +110,12 @@ public: * * Example: * // Find all tanks with armor > 150 - * queryByProperty("armor", [](const json& val) { - * return val.is_number() && val.get() > 150; + * queryByProperty("armor", [](const IDataValue& val) { + * return val.isNumber() && val.asInt() > 150; * }); */ virtual std::vector queryByProperty(const std::string& propName, - const std::function& predicate) = 0; + const std::function& predicate) = 0; // ======================================== // NODE'S OWN DATA @@ -125,9 +123,9 @@ public: /** * @brief Get this node's data blob - * @return JSON data or empty JSON if no data + * @return Data value or null if no data */ - virtual json getData() const = 0; + virtual std::unique_ptr getData() const = 0; /** * @brief Check if this node has data @@ -137,9 +135,9 @@ public: /** * @brief Set this node's data - * @param data JSON data to set + * @param data Data to set */ - virtual void setData(const json& data) = 0; + virtual void setData(std::unique_ptr data) = 0; // ======================================== // TYPED DATA ACCESS BY PROPERTY NAME diff --git a/include/grove/IDataValue.h b/include/grove/IDataValue.h new file mode 100644 index 0000000..b361281 --- /dev/null +++ b/include/grove/IDataValue.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +namespace warfactory { + +/** + * @brief Interface for data values - abstracts underlying data format + * + * This allows IDataNode to work with values without exposing JSON directly. + * Concrete implementations can use JSON, MessagePack, or any other format. + */ +class IDataValue { +public: + virtual ~IDataValue() = default; + + // Type checking + virtual bool isNull() const = 0; + virtual bool isBool() const = 0; + virtual bool isNumber() const = 0; + virtual bool isString() const = 0; + virtual bool isArray() const = 0; + virtual bool isObject() const = 0; + + // Value access with defaults + virtual bool asBool(bool defaultValue = false) const = 0; + virtual int asInt(int defaultValue = 0) const = 0; + virtual double asDouble(double defaultValue = 0.0) const = 0; + virtual std::string asString(const std::string& defaultValue = "") const = 0; + + // Array/Object access + virtual size_t size() const = 0; + virtual std::unique_ptr get(size_t index) const = 0; + virtual std::unique_ptr get(const std::string& key) const = 0; + virtual bool has(const std::string& key) const = 0; + + // Serialization + virtual std::string toString() const = 0; +}; + +} // namespace warfactory diff --git a/include/grove/IEngine.h b/include/grove/IEngine.h index 9f1a236..c3e40e0 100644 --- a/include/grove/IEngine.h +++ b/include/grove/IEngine.h @@ -2,15 +2,13 @@ #include #include -#include // Forward declarations to avoid circular dependencies namespace warfactory { class IModuleSystem; + class IIO; } -using json = nlohmann::json; - namespace warfactory { enum class EngineType { diff --git a/include/grove/IIO.h b/include/grove/IIO.h index 0f3edae..f3fddbe 100644 --- a/include/grove/IIO.h +++ b/include/grove/IIO.h @@ -3,9 +3,8 @@ #include #include #include -#include - -using json = nlohmann::json; +#include +#include "IDataNode.h" namespace warfactory { @@ -24,7 +23,7 @@ struct SubscriptionConfig { struct Message { std::string topic; - json data; + std::unique_ptr data; uint64_t timestamp; }; @@ -55,9 +54,9 @@ public: /** * @brief Publish message to a topic * @param topic Topic name (e.g., "player:123", "economy:prices") - * @param message JSON message data + * @param message Message data */ - virtual void publish(const std::string& topic, const json& message) = 0; + virtual void publish(const std::string& topic, std::unique_ptr message) = 0; /** * @brief Subscribe to topic pattern (high-frequency) diff --git a/include/grove/IModule.h b/include/grove/IModule.h index e5f600c..2fdf792 100644 --- a/include/grove/IModule.h +++ b/include/grove/IModule.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include "IDataNode.h" #include "ITaskScheduler.h" @@ -10,8 +10,6 @@ namespace warfactory { class IIO; } -using json = nlohmann::json; - namespace warfactory { @@ -43,13 +41,13 @@ public: /** * @brief Process game logic - * @param input JSON input from other modules or the module system + * @param input Data input from other modules or the module system * * This is the core method where all module logic is implemented. * Modules communicate via IIO pub/sub and can delegate tasks via ITaskScheduler. * Must handle state properly through getState/setState for hot-reload. */ - virtual void process(const json& input) = 0; + virtual void process(const IDataNode& input) = 0; /** * @brief Set module configuration (replaces initialize) @@ -70,9 +68,9 @@ public: /** * @brief Get detailed health status of the module - * @return JSON health report with status, metrics, and diagnostics + * @return Health report with status, metrics, and diagnostics */ - virtual json getHealthStatus() = 0; + virtual std::unique_ptr getHealthStatus() = 0; /** * @brief Cleanup and shutdown the module @@ -84,24 +82,24 @@ public: /** * @brief Get current module state for hot-reload support - * @return JSON representation of all module state + * @return Data representation of all module state * * Critical for hot-reload functionality. Must serialize all internal * state that needs to be preserved when the module is replaced. - * The returned JSON should be sufficient to restore the module to + * The returned data should be sufficient to restore the module to * its current state via setState(). */ - virtual json getState() = 0; + virtual std::unique_ptr getState() = 0; /** * @brief Restore module state after hot-reload - * @param state JSON state previously returned by getState() + * @param state State previously returned by getState() * * Called after module replacement to restore the previous state. - * Must be able to reconstruct all internal state from the JSON + * Must be able to reconstruct all internal state from the data * to ensure seamless hot-reload without game disruption. */ - virtual void setState(const json& state) = 0; + virtual void setState(const IDataNode& state) = 0; /** * @brief Get module type identifier diff --git a/include/grove/IModuleSystem.h b/include/grove/IModuleSystem.h index e618912..911a018 100644 --- a/include/grove/IModuleSystem.h +++ b/include/grove/IModuleSystem.h @@ -2,7 +2,6 @@ #include #include -#include #include "ITaskScheduler.h" // Forward declarations to avoid circular dependencies @@ -11,8 +10,6 @@ namespace warfactory { class IIO; } -using json = nlohmann::json; - namespace warfactory { enum class ModuleSystemType { @@ -76,14 +73,14 @@ public: /** * @brief Query a specific module directly * @param name Name of the module to query - * @param input JSON input to send to the module - * @return JSON response from the module + * @param input Input data to send to the module + * @return Response data from the module * * This provides direct access to module functionality for debugging, * testing, or administrative purposes. The query bypasses normal * execution flow and calls the module's process() method directly. */ - virtual json queryModule(const std::string& name, const json& input) = 0; + virtual std::unique_ptr queryModule(const std::string& name, const IDataNode& input) = 0; /** * @brief Get module system type identifier diff --git a/include/grove/ISerializable.h b/include/grove/ISerializable.h index d66739d..78a5a2a 100644 --- a/include/grove/ISerializable.h +++ b/include/grove/ISerializable.h @@ -1,8 +1,7 @@ #pragma once -#include - -using json = nlohmann::json; +#include "IDataNode.h" +#include namespace warfactory { @@ -10,8 +9,8 @@ class ISerializable { public: virtual ~ISerializable() = default; - virtual json serialize() const = 0; - virtual void deserialize(const json& data) = 0; + virtual std::unique_ptr serialize() const = 0; + virtual void deserialize(const IDataNode& data) = 0; }; } // namespace warfactory \ No newline at end of file diff --git a/include/grove/ITaskScheduler.h b/include/grove/ITaskScheduler.h index d92aa7b..bbdbb0a 100644 --- a/include/grove/ITaskScheduler.h +++ b/include/grove/ITaskScheduler.h @@ -1,9 +1,8 @@ #pragma once #include -#include - -using json = nlohmann::json; +#include +#include "IDataNode.h" namespace warfactory { @@ -43,26 +42,28 @@ public: /** * @brief Schedule a task for execution * @param taskType Type of task (e.g., "pathfinding", "market_analysis", "belt_optimization") - * @param taskData JSON data for the task + * @param taskData Data for the task * * Example usage: * ```cpp * // TankModule delegates pathfinding - * scheduler->scheduleTask("pathfinding", { + * auto taskData = createDataNode({ * {"start", {x: 100, y: 200}}, * {"target", {x: 500, y: 600}}, * {"unit_id", "tank_001"} * }); + * scheduler->scheduleTask("pathfinding", std::move(taskData)); * * // ProductionModule delegates belt calculation - * scheduler->scheduleTask("belt_optimization", { + * auto beltData = createDataNode({ * {"factory_id", "main_base"}, * {"item_type", "iron_plate"}, * {"throughput_target", 240} * }); + * scheduler->scheduleTask("belt_optimization", std::move(beltData)); * ``` */ - virtual void scheduleTask(const std::string& taskType, const json& taskData) = 0; + virtual void scheduleTask(const std::string& taskType, std::unique_ptr taskData) = 0; /** * @brief Check if completed tasks are available @@ -75,7 +76,7 @@ public: /** * @brief Pull and consume one completed task - * @return Task result JSON. Task is removed from completed queue. + * @return Task result data. Task is removed from completed queue. * * Example results: * ```cpp @@ -96,7 +97,7 @@ public: * } * ``` */ - virtual json getCompletedTask() = 0; + virtual std::unique_ptr getCompletedTask() = 0; }; } // namespace warfactory \ No newline at end of file diff --git a/src/DebugEngine.cpp b/src/DebugEngine.cpp index dca6b2c..b70a80c 100644 --- a/src/DebugEngine.cpp +++ b/src/DebugEngine.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/src/EngineFactory.cpp b/src/EngineFactory.cpp index 1127cf7..fc7a586 100644 --- a/src/EngineFactory.cpp +++ b/src/EngineFactory.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/src/IOFactory.cpp b/src/IOFactory.cpp index e373fc9..0b71072 100644 --- a/src/IOFactory.cpp +++ b/src/IOFactory.cpp @@ -1,12 +1,12 @@ -#include +#include #include #include #include #include // Include implemented transports -#include -#include +#include +#include // Forward declarations for future implementations // #include "LocalIO.h" // #include "NetworkIO.h" diff --git a/src/ImGuiUI.cpp b/src/ImGuiUI.cpp index 7fd2715..43a216d 100644 --- a/src/ImGuiUI.cpp +++ b/src/ImGuiUI.cpp @@ -1,4 +1,4 @@ -#include "warfactory/ImGuiUI.h" +#include #include #include #include diff --git a/src/IntraIO.cpp b/src/IntraIO.cpp index 640aa38..bd27225 100644 --- a/src/IntraIO.cpp +++ b/src/IntraIO.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include diff --git a/src/IntraIOManager.cpp b/src/IntraIOManager.cpp index ebfd646..b7b1be8 100644 --- a/src/IntraIOManager.cpp +++ b/src/IntraIOManager.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #include #include diff --git a/src/ModuleFactory.cpp b/src/ModuleFactory.cpp index dfe54db..0df0228 100644 --- a/src/ModuleFactory.cpp +++ b/src/ModuleFactory.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/src/ModuleSystemFactory.cpp b/src/ModuleSystemFactory.cpp index 1f63220..ab31fdb 100644 --- a/src/ModuleSystemFactory.cpp +++ b/src/ModuleSystemFactory.cpp @@ -1,10 +1,10 @@ -#include +#include #include #include #include // Include implemented systems -#include +#include // Forward declarations for future implementations // #include "ThreadedModuleSystem.h" // #include "ThreadPoolModuleSystem.h" diff --git a/src/ResourceRegistry.cpp b/src/ResourceRegistry.cpp index 5331e52..cf3e35c 100644 --- a/src/ResourceRegistry.cpp +++ b/src/ResourceRegistry.cpp @@ -1,4 +1,4 @@ -#include "warfactory/ResourceRegistry.h" +#include #include namespace warfactory { diff --git a/src/SequentialModuleSystem.cpp b/src/SequentialModuleSystem.cpp index f6badbb..81def4a 100644 --- a/src/SequentialModuleSystem.cpp +++ b/src/SequentialModuleSystem.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include