Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
GraphMigrator.cpp
Go to the documentation of this file.
1/**
2 * @file GraphMigrator.cpp
3 * @brief Implementation of GraphMigrator
4 * @author Olympe Engine
5 * @date 2026-02-18
6 */
7
8#include "GraphMigrator.h"
9#include "../system/system_utils.h"
10
12
13namespace Olympe {
14namespace NodeGraph {
15
16// ============================================================================
17// Public API
18// ============================================================================
19
21{
22 int version = DetectSchemaVersion(j);
23
24 if (version == 0)
25 {
26 // Legacy BT format
27 SYSTEM_LOG << "[GraphMigrator] Detected legacy BT format, migrating to v2..." << std::endl;
30 }
31 else if (version == 1)
32 {
33 // v1 Blueprint format
34 SYSTEM_LOG << "[GraphMigrator] Detected v1 Blueprint format, migrating to v2..." << std::endl;
37 }
38 else
39 {
40 // v2 unified format
41 SYSTEM_LOG << "[GraphMigrator] Using v2 format (no migration needed)" << std::endl;
43 }
44}
45
46// ============================================================================
47// Private Methods
48// ============================================================================
49
51{
52 // Check for explicit schema_version or schemaVersion
53 int schemaVersion = JsonHelper::GetInt(j, "schema_version", -1);
54 if (schemaVersion == -1)
55 {
56 schemaVersion = JsonHelper::GetInt(j, "schemaVersion", -1);
57 }
58
59 // If found and is 2, it's v2
60 if (schemaVersion == 2)
61 {
62 return 2;
63 }
64
65 // If found and is 1, it's v1
66 if (schemaVersion == 1)
67 {
68 return 1;
69 }
70
71 // Check for v1 Blueprint format (has "blueprint_version")
72 if (j.contains("blueprint_version"))
73 {
74 return 1;
75 }
76
77 // Check for v2 format markers
78 if (j.contains("graphKind") && j.contains("data"))
79 {
80 return 2;
81 }
82
83 // Otherwise assume legacy BT format
84 return 0;
85}
86
88{
89 json v2 = json::object();
90
91 v2["schemaVersion"] = 2;
92 v2["type"] = "AIGraph";
93 v2["graphKind"] = "BehaviorTree";
94
95 // Metadata
96 json metadata = json::object();
97 metadata["author"] = JsonHelper::GetString(v1, "author", "Unknown");
98 metadata["created"] = JsonHelper::GetString(v1, "created", "");
99
100 json tags = json::array();
101 tags.push_back("AI");
102 tags.push_back("BehaviorTree");
103 metadata["tags"] = tags;
104
105 v2["metadata"] = metadata;
106
107 // Editor state
108 json editorState = json::object();
109 editorState["zoom"] = JsonHelper::GetFloat(v1, "zoom", 1.0f);
110
111 json scrollOffset = json::object();
112 if (v1.contains("editorState") && v1["editorState"].is_object())
113 {
114 const json& es = v1["editorState"];
115 if (es.contains("scrollOffset") && es["scrollOffset"].is_object())
116 {
117 scrollOffset["x"] = JsonHelper::GetFloat(es["scrollOffset"], "x", 0.0f);
118 scrollOffset["y"] = JsonHelper::GetFloat(es["scrollOffset"], "y", 0.0f);
119 }
120 else
121 {
122 scrollOffset["x"] = 0.0f;
123 scrollOffset["y"] = 0.0f;
124 }
125 }
126 else
127 {
128 scrollOffset["x"] = 0.0f;
129 scrollOffset["y"] = 0.0f;
130 }
131 editorState["scrollOffset"] = scrollOffset;
132
133 json selectedNodes = json::array();
134 editorState["selectedNodes"] = selectedNodes;
135 editorState["layoutDirection"] = "TopToBottom";
136
137 v2["editorState"] = editorState;
138
139 // Data section
140 json data = json::object();
141 data["rootNodeId"] = JsonHelper::GetInt(v1, "rootNodeId", 0);
142
143 // Nodes
144 json nodesArray = json::array();
145 if (v1.contains("nodes") && v1["nodes"].is_array())
146 {
147 const json& oldNodes = v1["nodes"];
148 for (size_t i = 0; i < oldNodes.size(); ++i)
149 {
150 const json& oldNode = oldNodes[i];
151
152 json newNode = json::object();
153 newNode["id"] = JsonHelper::GetInt(oldNode, "id", 0);
154 newNode["type"] = JsonHelper::GetString(oldNode, "type", "");
155 newNode["name"] = JsonHelper::GetString(oldNode, "name", "");
156
157 json position = json::object();
158 if (oldNode.contains("position") && oldNode["position"].is_object())
159 {
160 position["x"] = JsonHelper::GetFloat(oldNode["position"], "x", 0.0f);
161 position["y"] = JsonHelper::GetFloat(oldNode["position"], "y", 0.0f);
162 }
163 else
164 {
165 position["x"] = 0.0f;
166 position["y"] = 0.0f;
167 }
168 newNode["position"] = position;
169
170 // Children - handle both "children" and "childIds"
171 json children = json::array();
172 if (oldNode.contains("children") && oldNode["children"].is_array())
173 {
174 const json& childrenArr = oldNode["children"];
175 for (size_t c = 0; c < childrenArr.size(); ++c)
176 {
177 children.push_back(childrenArr[c]);
178 }
179 }
180 else if (oldNode.contains("childIds") && oldNode["childIds"].is_array())
181 {
182 const json& childrenArr = oldNode["childIds"];
183 for (size_t c = 0; c < childrenArr.size(); ++c)
184 {
185 children.push_back(childrenArr[c]);
186 }
187 }
188 newNode["children"] = children;
189
190 // Parameters
191 json parameters = json::object();
192 if (oldNode.contains("parameters") && oldNode["parameters"].is_object())
193 {
194 const json& params = oldNode["parameters"];
195 for (auto it = params.begin(); it != params.end(); ++it)
196 {
197 parameters[it.key()] = it.value();
198 }
199 }
200 newNode["parameters"] = parameters;
201
202 // Decorator child
203 int decoratorChild = JsonHelper::GetInt(oldNode, "decoratorChildId", 0);
204 if (decoratorChild != 0)
205 {
206 newNode["decoratorChildId"] = decoratorChild;
207 }
208
209 nodesArray.push_back(newNode);
210 }
211 }
212 data["nodes"] = nodesArray;
213
214 // Links
215 json linksArray = json::array();
216 if (v1.contains("links") && v1["links"].is_array())
217 {
218 const json& oldLinks = v1["links"];
219 for (size_t i = 0; i < oldLinks.size(); ++i)
220 {
221 const json& oldLink = oldLinks[i];
222
223 json newLink = json::object();
224 newLink["id"] = JsonHelper::GetInt(oldLink, "id", 0);
225
226 json fromPin = json::object();
227 if (oldLink.contains("fromPin") && oldLink["fromPin"].is_object())
228 {
229 fromPin["nodeId"] = JsonHelper::GetInt(oldLink["fromPin"], "nodeId", 0);
230 fromPin["pinId"] = JsonHelper::GetString(oldLink["fromPin"], "pinId", "output");
231 }
232 newLink["fromPin"] = fromPin;
233
234 json toPin = json::object();
235 if (oldLink.contains("toPin") && oldLink["toPin"].is_object())
236 {
237 toPin["nodeId"] = JsonHelper::GetInt(oldLink["toPin"], "nodeId", 0);
238 toPin["pinId"] = JsonHelper::GetString(oldLink["toPin"], "pinId", "input");
239 }
240 newLink["toPin"] = toPin;
241
242 linksArray.push_back(newLink);
243 }
244 }
245 data["links"] = linksArray;
246
247 v2["data"] = data;
248
249 return v2;
250}
251
253{
254 // For now, just wrap it in v2 format
255 // A full blueprint migration would need more complex logic
256
257 json v2 = json::object();
258
259 v2["schemaVersion"] = 2;
260 v2["type"] = JsonHelper::GetString(v1, "type", "Blueprint");
261 v2["graphKind"] = "Blueprint";
262
263 // Copy metadata if exists
264 if (v1.contains("metadata") && v1["metadata"].is_object())
265 {
266 v2["metadata"] = v1["metadata"];
267 }
268 else
269 {
270 json metadata = json::object();
271 metadata["author"] = "Unknown";
272 v2["metadata"] = metadata;
273 }
274
275 // Editor state
276 json editorState = json::object();
277 editorState["zoom"] = 1.0f;
278
279 json scrollOffset = json::object();
280 scrollOffset["x"] = 0.0f;
281 scrollOffset["y"] = 0.0f;
282 editorState["scrollOffset"] = scrollOffset;
283
284 json selectedNodes = json::array();
285 editorState["selectedNodes"] = selectedNodes;
286 editorState["layoutDirection"] = "TopToBottom";
287
288 v2["editorState"] = editorState;
289
290 // Data - try to preserve original structure
291 if (v1.contains("data") && v1["data"].is_object())
292 {
293 v2["data"] = v1["data"];
294 }
295 else
296 {
297 json data = json::object();
298 data["rootNodeId"] = 0;
299 data["nodes"] = json::array();
300 data["links"] = json::array();
301 v2["data"] = data;
302 }
303
304 return v2;
305}
306
307} // namespace NodeGraph
308} // namespace Olympe
nlohmann::json json
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Migration system for JSON versions.
Main document class for a node graph.
static GraphDocument FromJson(const json &j)
Create graph from JSON.
static GraphDocument LoadWithMigration(const json &j)
Load graph with automatic migration.
static int DetectSchemaVersion(const json &j)
Detect schema version.
static json MigrateV1BlueprintToV2(const json &v1)
Migrate v1 Blueprint format to v2.
static json MigrateLegacyBTToV2(const json &v1)
Migrate legacy BT format to v2.
std::string GetString(const json &j, const std::string &key, const std::string &defaultValue="")
Safely get a string value from JSON.
int GetInt(const json &j, const std::string &key, int defaultValue=0)
Safely get an integer value from JSON.
float GetFloat(const json &j, const std::string &key, float defaultValue=0.0f)
Safely get a float value from JSON.
< Provides AssetID and INVALID_ASSET_ID
nlohmann::json json
nlohmann::json json
#define SYSTEM_LOG