Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
VisualScriptEditorPanel_Verification_new.cpp
Go to the documentation of this file.
1void VisualScriptEditorPanel::RunGraphSimulation()
2{
3 SYSTEM_LOG << "[VisualScriptEditorPanel] RunGraphSimulation() called for graph '"
4 << m_template.Name << "'\n";
5
6 // Clear previous simulation traces and token stack
7 m_simulationTraces.clear();
8 m_executionTokenStack.clear();
9
10 m_simulationTraces.push_back("[SIMULATION] Graph execution simulation started");
11 m_simulationTraces.push_back("[SIMULATION] Graph: " + m_template.Name);
12 m_simulationTraces.push_back("[SIMULATION] Total nodes: " + std::to_string(m_template.Nodes.size()));
13 m_simulationTraces.push_back("[SIMULATION] Total connections: " + std::to_string(m_template.ExecConnections.size()));
14 m_simulationTraces.push_back("[SIMULATION] Blackboard entries: " + std::to_string(m_template.Blackboard.size()));
15
16 m_simulationTraces.push_back("");
17 m_simulationTraces.push_back("=== EXECUTION TRACE ===");
18
19 // Initialize blackboard with default values
20 std::map<std::string, TaskValue> blackboard;
21 for (size_t i = 0; i < m_template.Blackboard.size(); ++i)
22 {
23 blackboard[m_template.Blackboard[i].Key] = m_template.Blackboard[i].Default;
24 }
25
26 // Find entry point and push initial token
27 int32_t entryPointID = m_template.EntryPointID != NODE_INDEX_NONE ?
28 m_template.EntryPointID : m_template.RootNodeID;
29
30 if (entryPointID == NODE_INDEX_NONE)
31 {
32 m_simulationTraces.push_back("[ERROR] No entry point or root node found!");
33 SYSTEM_LOG << "[VisualScriptEditorPanel] Simulation FAILED: No entry point\n";
34 m_simulationDone = true;
35 return;
36 }
37
38 m_simulationTraces.push_back("[START] Entry point: Node #" + std::to_string(entryPointID));
39
40 // Phase 24.4 — Token-based execution stack
41 m_executionTokenStack.push_back(ExecutionToken(entryPointID, 0));
42
43 int stepCount = 0;
44 int maxSteps = 200; // Increased for multi-branch scenarios
45 std::unordered_set<std::string> executionPaths; // Track (nodeID, depth) to detect cycles
46
47 while (!m_executionTokenStack.empty() && stepCount < maxSteps)
48 {
49 // Pop the next token from the stack
50 ExecutionToken currentToken = m_executionTokenStack.back();
51 m_executionTokenStack.pop_back();
52
54 int depth = currentToken.depth;
55
56 // Find node definition
57 const TaskNodeDefinition* nodePtr = nullptr;
58 for (size_t i = 0; i < m_template.Nodes.size(); ++i)
59 {
60 if (m_template.Nodes[i].NodeID == currentNodeID)
61 {
62 nodePtr = &m_template.Nodes[i];
63 break;
64 }
65 }
66
67 if (!nodePtr)
68 {
69 m_simulationTraces.push_back("[ERROR] Node #" + std::to_string(currentNodeID) + " not found in template!");
70 continue;
71 }
72
73 // Detect infinite loops at same depth
74 std::string pathKey = std::to_string(currentNodeID) + ":" + std::to_string(depth);
75 if (executionPaths.count(pathKey) > 0)
76 {
77 m_simulationTraces.push_back("[CYCLE] WARNING: Cycle detected at Node #" + std::to_string(currentNodeID));
78 continue;
79 }
80 executionPaths.insert(pathKey);
81
82 // Trace node entry
83 std::ostringstream nodeEntry;
84 nodeEntry << "[ENTER] Step " << (stepCount + 1) << ": Node #" << nodePtr->NodeID;
85 if (!nodePtr->NodeName.empty())
86 nodeEntry << " '" << nodePtr->NodeName << "'";
87 if (depth > 0)
88 nodeEntry << " [depth:" << depth << "]";
89 m_simulationTraces.push_back(nodeEntry.str());
90
91 // Handle each node type and push next tokens
92 std::vector<int32_t> nextNodeIDs; // Multiple outputs for branching
93
94 switch (nodePtr->Type)
95 {
96 case TaskNodeType::EntryPoint:
97 {
98 m_simulationTraces.push_back(" ├─ [EVAL] EntryPoint - start of graph");
99 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
100 {
101 if (m_template.ExecConnections[i].SourceNodeID == currentNodeID)
102 {
103 nextNodeIDs.push_back(m_template.ExecConnections[i].TargetNodeID);
104 break;
105 }
106 }
107 m_simulationTraces.push_back(" └─ [RESULT] EntryPoint completed");
108 break;
109 }
110
111 case TaskNodeType::GetBBValue:
112 {
113 m_simulationTraces.push_back(" ├─ [EVAL] GetBBValue node");
114 m_simulationTraces.push_back(" │ Key: '" + nodePtr->BBKey + "'");
115 auto it = blackboard.find(nodePtr->BBKey);
116 if (it != blackboard.end())
117 {
118 m_simulationTraces.push_back(" │ Value: " + it->second.AsString());
119 }
120 else
121 {
122 m_simulationTraces.push_back(" │ Value: [NOT FOUND]");
123 }
124 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
125 {
126 if (m_template.ExecConnections[i].SourceNodeID == currentNodeID)
127 {
128 nextNodeIDs.push_back(m_template.ExecConnections[i].TargetNodeID);
129 break;
130 }
131 }
132 m_simulationTraces.push_back(" └─ [RESULT] Read value from blackboard");
133 break;
134 }
135
136 case TaskNodeType::SetBBValue:
137 {
138 m_simulationTraces.push_back(" ├─ [EVAL] SetBBValue node");
139 m_simulationTraces.push_back(" │ Key: '" + nodePtr->BBKey + "'");
140 m_simulationTraces.push_back(" │ Setting value in blackboard (simulated)");
141 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
142 {
143 if (m_template.ExecConnections[i].SourceNodeID == currentNodeID)
144 {
145 nextNodeIDs.push_back(m_template.ExecConnections[i].TargetNodeID);
146 break;
147 }
148 }
149 m_simulationTraces.push_back(" └─ [RESULT] Value written to blackboard");
150 break;
151 }
152
153 case TaskNodeType::MathOp:
154 {
155 m_simulationTraces.push_back(" ├─ [EVAL] MathOp node");
156 m_simulationTraces.push_back(" │ Operator: '" + nodePtr->MathOperator + "'");
157 std::ostringstream leftOp, rightOp;
158 if (nodePtr->mathOpRef.leftOperand.mode == MathOpOperand::Mode::Variable)
159 leftOp << "Variable: " << nodePtr->mathOpRef.leftOperand.variableName;
160 else if (nodePtr->mathOpRef.leftOperand.mode == MathOpOperand::Mode::Const)
161 leftOp << "Const: " << nodePtr->mathOpRef.leftOperand.constValue;
162 else
163 leftOp << "Pin: [input]";
164 if (nodePtr->mathOpRef.rightOperand.mode == MathOpOperand::Mode::Variable)
165 rightOp << "Variable: " << nodePtr->mathOpRef.rightOperand.variableName;
166 else if (nodePtr->mathOpRef.rightOperand.mode == MathOpOperand::Mode::Const)
167 rightOp << "Const: " << nodePtr->mathOpRef.rightOperand.constValue;
168 else
169 rightOp << "Pin: [input]";
170 m_simulationTraces.push_back(" │ Left: " + leftOp.str());
171 m_simulationTraces.push_back(" │ Right: " + rightOp.str());
172 m_simulationTraces.push_back(" │ Result: [computed]");
173 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
174 {
175 if (m_template.ExecConnections[i].SourceNodeID == currentNodeID)
176 {
177 nextNodeIDs.push_back(m_template.ExecConnections[i].TargetNodeID);
178 break;
179 }
180 }
181 m_simulationTraces.push_back(" └─ [RESULT] Math operation executed");
182 break;
183 }
184
185 case TaskNodeType::Branch:
186 {
187 m_simulationTraces.push_back(" ├─ [EVAL] Branch node");
188 m_simulationTraces.push_back(" │ Evaluating condition...");
189 bool conditionResult = true; // Simplified: always true
190 m_simulationTraces.push_back(" │ Condition result: " + std::string(conditionResult ? "TRUE" : "FALSE"));
191 const std::string targetPin = conditionResult ? "Out" : "OutElse";
192 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
193 {
194 const ExecPinConnection& conn = m_template.ExecConnections[i];
195 if (conn.SourceNodeID == currentNodeID &&
196 (conn.SourcePinName == targetPin || conn.SourcePinName.empty()))
197 {
198 nextNodeIDs.push_back(conn.TargetNodeID);
199 break;
200 }
201 }
202 m_simulationTraces.push_back(" └─ [RESULT] Branch taken: " + targetPin);
203 break;
204 }
205
206 case TaskNodeType::Switch:
207 {
208 m_simulationTraces.push_back(" ├─ [EVAL] Switch node");
209 m_simulationTraces.push_back(" │ Variable: '" + nodePtr->switchVariable + "'");
210 m_simulationTraces.push_back(" │ Cases: " + std::to_string(nodePtr->switchCases.size()));
211 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
212 {
213 if (m_template.ExecConnections[i].SourceNodeID == currentNodeID)
214 {
215 nextNodeIDs.push_back(m_template.ExecConnections[i].TargetNodeID);
216 m_simulationTraces.push_back(" │ Case selected: (first available)");
217 break;
218 }
219 }
220 m_simulationTraces.push_back(" └─ [RESULT] Switch case executed");
221 break;
222 }
223
224 case TaskNodeType::Delay:
225 {
226 m_simulationTraces.push_back(" ├─ [EVAL] Delay node");
227 m_simulationTraces.push_back(" │ Duration: " + std::to_string(nodePtr->DelaySeconds) + " seconds");
228 m_simulationTraces.push_back(" │ (Delay simulated)");
229 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
230 {
231 if (m_template.ExecConnections[i].SourceNodeID == currentNodeID)
232 {
233 nextNodeIDs.push_back(m_template.ExecConnections[i].TargetNodeID);
234 break;
235 }
236 }
237 m_simulationTraces.push_back(" └─ [RESULT] Delay completed");
238 break;
239 }
240
241 case TaskNodeType::AtomicTask:
242 {
243 m_simulationTraces.push_back(" ├─ [EVAL] AtomicTask node");
244 m_simulationTraces.push_back(" │ Task type: '" + nodePtr->AtomicTaskID + "'");
245 m_simulationTraces.push_back(" │ (Task execution simulated)");
246 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
247 {
248 const ExecPinConnection& conn = m_template.ExecConnections[i];
249 if (conn.SourceNodeID == currentNodeID && conn.SourcePinName == "Completed")
250 {
251 nextNodeIDs.push_back(conn.TargetNodeID);
252 break;
253 }
254 }
255 m_simulationTraces.push_back(" └─ [RESULT] Task completed");
256 break;
257 }
258
259 case TaskNodeType::VSSequence:
260 {
261 m_simulationTraces.push_back(" ├─ [EVAL] Sequence node");
262
263 // Phase 24.4 — Collect ALL outgoing connections and push them as tokens (FIFO order, so reverse push)
264 std::vector<ExecPinConnection> sequenceOutputs;
265 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
266 {
267 if (m_template.ExecConnections[i].SourceNodeID == currentNodeID)
268 {
269 sequenceOutputs.push_back(m_template.ExecConnections[i]);
270 }
271 }
272
273 m_simulationTraces.push_back(" │ Output pins: " + std::to_string(sequenceOutputs.size()));
274 m_simulationTraces.push_back(" │ Queueing " + std::to_string(sequenceOutputs.size()) + " branches:");
275
276 // Push tokens in REVERSE order so first output is executed first (LIFO stack semantics)
277 for (int oi = (int)sequenceOutputs.size() - 1; oi >= 0; --oi)
278 {
279 const ExecPinConnection& outConn = sequenceOutputs[oi];
280 const TaskNodeDefinition* outNodePtr = nullptr;
281 for (size_t i = 0; i < m_template.Nodes.size(); ++i)
282 {
283 if (m_template.Nodes[i].NodeID == outConn.TargetNodeID)
284 {
285 outNodePtr = &m_template.Nodes[i];
286 break;
287 }
288 }
289
290 std::ostringstream outTrace;
291 outTrace << " ├─ Output [" << (oi + 1) << "] -> Node #" << outConn.TargetNodeID;
292 if (outNodePtr && !outNodePtr->NodeName.empty())
293 outTrace << " '" << outNodePtr->NodeName << "'";
294 m_simulationTraces.push_back(outTrace.str());
295
296 // Push token for this branch (with increased depth)
297 m_executionTokenStack.push_back(ExecutionToken(outConn.TargetNodeID, depth + 1));
298 }
299
300 m_simulationTraces.push_back(" └─ [RESULT] Queued " + std::to_string(sequenceOutputs.size()) + " branch tokens");
301 break;
302 }
303
304 case TaskNodeType::While:
305 {
306 m_simulationTraces.push_back(" ├─ [EVAL] While loop node");
307 m_simulationTraces.push_back(" │ Evaluating condition...");
308 m_simulationTraces.push_back(" │ Condition result: TRUE (Loop continues)");
309 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
310 {
311 const ExecPinConnection& conn = m_template.ExecConnections[i];
312 if (conn.SourceNodeID == currentNodeID && conn.SourcePinName == "Loop")
313 {
314 nextNodeIDs.push_back(conn.TargetNodeID);
315 break;
316 }
317 }
318 m_simulationTraces.push_back(" └─ [RESULT] Loop iteration");
319 break;
320 }
321
322 default:
323 {
324 m_simulationTraces.push_back(" ├─ [EVAL] Node type: " + std::to_string(static_cast<int>(nodePtr->Type)));
325 for (size_t i = 0; i < m_template.ExecConnections.size(); ++i)
326 {
327 if (m_template.ExecConnections[i].SourceNodeID == currentNodeID)
328 {
329 nextNodeIDs.push_back(m_template.ExecConnections[i].TargetNodeID);
330 break;
331 }
332 }
333 m_simulationTraces.push_back(" └─ [RESULT] Node processed");
334 break;
335 }
336 }
337
338 // Trace node exit and push next tokens
339 if (!nextNodeIDs.empty())
340 {
341 for (size_t ni = 0; ni < nextNodeIDs.size(); ++ni)
342 {
343 m_simulationTraces.push_back("[EXIT] -> Next: Node #" + std::to_string(nextNodeIDs[ni]));
344 // Push in REVERSE order (LIFO) so first is executed first
345 m_executionTokenStack.push_back(ExecutionToken(nextNodeIDs[ni], depth));
346 }
347 }
348 else
349 {
350 m_simulationTraces.push_back("[EXIT] -> Branch ends (no next node)");
351 }
352
353 m_simulationTraces.push_back(""); // Blank line for readability
354 ++stepCount;
355 }
356
357 // Final summary
358 m_simulationTraces.push_back("=== EXECUTION SUMMARY ===");
359 if (stepCount >= maxSteps)
360 {
361 m_simulationTraces.push_back("[WARNING] Maximum steps reached (" + std::to_string(maxSteps) + ") - possible infinite loop");
362 }
363 else if (m_executionTokenStack.empty())
364 {
365 m_simulationTraces.push_back("[SUCCESS] All execution branches completed");
366 }
367 m_simulationTraces.push_back("Total steps executed: " + std::to_string(stepCount));
368 m_simulationTraces.push_back("Blackboard entries: " + std::to_string(blackboard.size()));
369 m_simulationTraces.push_back("Remaining tokens in stack: " + std::to_string(m_executionTokenStack.size()));
370
371 m_simulationDone = true;
372
373 SYSTEM_LOG << "[VisualScriptEditorPanel] Simulation completed: " << stepCount << " steps\n";
374
375 // Append simulation traces to verification logs
376 m_verificationLogs.push_back(""); // Separator
377 m_verificationLogs.push_back("--- SIMULATION EXECUTION TRACE (Token-based) ---");
378 m_verificationLogs.insert(m_verificationLogs.end(),
379 m_simulationTraces.begin(),
380 m_simulationTraces.end());
381}
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
constexpr int32_t NODE_INDEX_NONE
Sentinel value for "no node" in node index / ID fields.
#define SYSTEM_LOG