Refactor: Replace JSON with IDataNode abstraction in all interfaces

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 <noreply@anthropic.com>
This commit is contained in:
StillHammer 2025-10-28 12:38:11 +08:00
parent e1cfa4513e
commit c01e00559b
20 changed files with 110 additions and 73 deletions

View File

@ -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()

View File

@ -1,9 +1,8 @@
#pragma once
#include <nlohmann/json.hpp>
#include "IDataNode.h"
#include <string>
using json = nlohmann::json;
#include <memory>
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<IDataNode> serialize() const = 0;
virtual void deserialize(const IDataNode& data) = 0;
protected:
void registerForSerialization();

View File

@ -4,18 +4,16 @@
#include <vector>
#include <memory>
#include <functional>
#include <nlohmann/json.hpp>
#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<int>() > 150;
* queryByProperty("armor", [](const IDataValue& val) {
* return val.isNumber() && val.asInt() > 150;
* });
*/
virtual std::vector<IDataNode*> queryByProperty(const std::string& propName,
const std::function<bool(const json&)>& predicate) = 0;
const std::function<bool(const IDataValue&)>& 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<IDataValue> 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<IDataValue> data) = 0;
// ========================================
// TYPED DATA ACCESS BY PROPERTY NAME

View File

@ -0,0 +1,43 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
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<IDataValue> get(size_t index) const = 0;
virtual std::unique_ptr<IDataValue> 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

View File

@ -2,15 +2,13 @@
#include <string>
#include <memory>
#include <nlohmann/json.hpp>
// Forward declarations to avoid circular dependencies
namespace warfactory {
class IModuleSystem;
class IIO;
}
using json = nlohmann::json;
namespace warfactory {
enum class EngineType {

View File

@ -3,9 +3,8 @@
#include <string>
#include <vector>
#include <functional>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
#include <memory>
#include "IDataNode.h"
namespace warfactory {
@ -24,7 +23,7 @@ struct SubscriptionConfig {
struct Message {
std::string topic;
json data;
std::unique_ptr<IDataNode> 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<IDataNode> message) = 0;
/**
* @brief Subscribe to topic pattern (high-frequency)

View File

@ -1,7 +1,7 @@
#pragma once
#include <string>
#include <nlohmann/json.hpp>
#include <memory>
#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<IDataNode> 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<IDataNode> 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

View File

@ -2,7 +2,6 @@
#include <string>
#include <memory>
#include <nlohmann/json.hpp>
#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<IDataNode> queryModule(const std::string& name, const IDataNode& input) = 0;
/**
* @brief Get module system type identifier

View File

@ -1,8 +1,7 @@
#pragma once
#include <nlohmann/json.hpp>
using json = nlohmann::json;
#include "IDataNode.h"
#include <memory>
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<IDataNode> serialize() const = 0;
virtual void deserialize(const IDataNode& data) = 0;
};
} // namespace warfactory

View File

@ -1,9 +1,8 @@
#pragma once
#include <string>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
#include <memory>
#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<IDataNode> 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<IDataNode> getCompletedTask() = 0;
};
} // namespace warfactory

View File

@ -1,4 +1,4 @@
#include <warfactory/DebugEngine.h>
#include <grove/DebugEngine.h>
#include <fstream>
#include <filesystem>
#include <spdlog/sinks/stdout_color_sinks.h>

View File

@ -1,4 +1,4 @@
#include <warfactory/EngineFactory.h>
#include <grove/EngineFactory.h>
#include <fstream>
#include <algorithm>
#include <nlohmann/json.hpp>

View File

@ -1,12 +1,12 @@
#include <warfactory/IOFactory.h>
#include <grove/IOFactory.h>
#include <algorithm>
#include <random>
#include <functional>
#include <spdlog/sinks/stdout_color_sinks.h>
// Include implemented transports
#include <warfactory/IntraIO.h>
#include <warfactory/IntraIOManager.h>
#include <grove/IntraIO.h>
#include <grove/IntraIOManager.h>
// Forward declarations for future implementations
// #include "LocalIO.h"
// #include "NetworkIO.h"

View File

@ -1,4 +1,4 @@
#include "warfactory/ImGuiUI.h"
#include <grove/ImGuiUI.h>
#include <sstream>
#include <iomanip>
#include <iostream>

View File

@ -1,5 +1,5 @@
#include <warfactory/IntraIO.h>
#include <warfactory/IntraIOManager.h>
#include <grove/IntraIO.h>
#include <grove/IntraIOManager.h>
#include <stdexcept>
#include <algorithm>
#include <thread>

View File

@ -1,5 +1,5 @@
#include <warfactory/IntraIOManager.h>
#include <warfactory/IntraIO.h>
#include <grove/IntraIOManager.h>
#include <grove/IntraIO.h>
#include <stdexcept>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>

View File

@ -1,4 +1,4 @@
#include <warfactory/ModuleFactory.h>
#include <grove/ModuleFactory.h>
#include <filesystem>
#include <dlfcn.h>
#include <algorithm>

View File

@ -1,10 +1,10 @@
#include <warfactory/ModuleSystemFactory.h>
#include <grove/ModuleSystemFactory.h>
#include <algorithm>
#include <thread>
#include <spdlog/sinks/stdout_color_sinks.h>
// Include implemented systems
#include <warfactory/SequentialModuleSystem.h>
#include <grove/SequentialModuleSystem.h>
// Forward declarations for future implementations
// #include "ThreadedModuleSystem.h"
// #include "ThreadPoolModuleSystem.h"

View File

@ -1,4 +1,4 @@
#include "warfactory/ResourceRegistry.h"
#include <grove/ResourceRegistry.h>
#include <algorithm>
namespace warfactory {

View File

@ -1,4 +1,4 @@
#include <warfactory/SequentialModuleSystem.h>
#include <grove/SequentialModuleSystem.h>
#include <stdexcept>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>