Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
TaskGraphTemplate.cpp
Go to the documentation of this file.
1/**
2 * @file TaskGraphTemplate.cpp
3 * @brief Implementation of TaskGraphTemplate validation and lookup for the Atomic Task System
4 * @author Olympe Engine
5 * @date 2026-02-20
6 */
7
8#include "TaskGraphTemplate.h"
9
10#include <stdexcept>
11#include <string>
12
13#include "../system/system_utils.h"
14
15namespace Olympe {
16
17// ============================================================================
18// Validation
19// ============================================================================
20
22{
23 // Rule 1: Nodes must not be empty
24 if (Nodes.empty())
25 {
26 SYSTEM_LOG << "[TaskGraphTemplate] Validate failed: Nodes is empty" << std::endl;
27 return false;
28 }
29
30 // Build a temporary set of valid IDs for O(n) lookups
31 std::unordered_map<int32_t, bool> validIds;
32 for (size_t i = 0; i < Nodes.size(); ++i)
33 {
34 validIds[Nodes[i].NodeID] = true;
35 }
36
37 // Rule 2: RootNodeID must reference an existing node
38 // EXCEPTION: Allow RootNodeID = -1 for legacy/template files (schema v2)
39 // These can still be loaded and edited, just not executed
40 if (RootNodeID != -1 && validIds.find(RootNodeID) == validIds.end())
41 {
42 SYSTEM_LOG << "[TaskGraphTemplate] Validate failed: RootNodeID " << RootNodeID
43 << " does not reference a known node" << std::endl;
44 return false;
45 }
46
47 // Rule 3: All ChildrenIDs must reference existing nodes
48 for (size_t i = 0; i < Nodes.size(); ++i)
49 {
51 for (size_t c = 0; c < node.ChildrenIDs.size(); ++c)
52 {
53 int32_t childId = node.ChildrenIDs[c];
54 if (validIds.find(childId) == validIds.end())
55 {
56 SYSTEM_LOG << "[TaskGraphTemplate] Validate failed: Node " << node.NodeID
57 << " references unknown child " << childId << std::endl;
58 return false;
59 }
60 }
61 }
62
63 SYSTEM_LOG << "[TaskGraphTemplate] Validate passed for template '" << Name << "'" << std::endl;
64 return true;
65}
66
67// ============================================================================
68// Node lookup
69// ============================================================================
70
72{
73 auto it = m_nodeLookup.find(nodeId);
74 if (it == m_nodeLookup.end())
75 {
76 return nullptr;
77 }
78 return it->second;
79}
80
82{
83 m_nodeLookup.clear();
84
85 for (size_t i = 0; i < Nodes.size(); ++i)
86 {
87 m_nodeLookup[Nodes[i].NodeID] = &Nodes[i];
88 }
89
90 SYSTEM_LOG << "[TaskGraphTemplate] BuildLookupCache: " << m_nodeLookup.size()
91 << " entries for template '" << Name << "'" << std::endl;
92}
93
94// ============================================================================
95// Phase 24.3 - Poka-Yoke: Sanitize exec connections
96// ============================================================================
97
99{
100 int removedCount = 0;
101
102 // Helper: determine if a node type is data-pure (no exec pins)
103 auto IsDataPureNode = [](TaskNodeType type) -> bool {
104 return type == TaskNodeType::GetBBValue ||
105 type == TaskNodeType::MathOp;
106 };
107
108 // Helper: find node by ID
109 auto FindNode = [this](int32_t nodeID) -> TaskNodeDefinition* {
110 for (size_t i = 0; i < Nodes.size(); ++i)
111 {
112 if (Nodes[i].NodeID == nodeID)
113 return &Nodes[i];
114 }
115 return nullptr;
116 };
117
118 // Scan ExecConnections and remove invalid ones
119 std::vector<ExecPinConnection> validConnections;
120
121 for (size_t i = 0; i < ExecConnections.size(); ++i)
122 {
124
125 TaskNodeDefinition* srcNode = FindNode(conn.SourceNodeID);
126 TaskNodeDefinition* dstNode = FindNode(conn.TargetNodeID);
127
128 // Check 1: Source node must exist
129 if (srcNode == nullptr)
130 {
131 SYSTEM_LOG << "[TaskGraphTemplate] SanitizeExecConnections: Removing connection with invalid source node #"
132 << conn.SourceNodeID << "\n";
133 ++removedCount;
134 continue;
135 }
136
137 // Check 2: Destination node must exist
138 if (dstNode == nullptr)
139 {
140 SYSTEM_LOG << "[TaskGraphTemplate] SanitizeExecConnections: Removing connection with invalid destination node #"
141 << conn.TargetNodeID << "\n";
142 ++removedCount;
143 continue;
144 }
145
146 // Check 3: Source node cannot be data-pure (data-pure nodes have no exec-out)
147 if (IsDataPureNode(srcNode->Type))
148 {
149 SYSTEM_LOG << "[TaskGraphTemplate] SanitizeExecConnections: Removing exec-out connection from data-pure node #"
150 << conn.SourceNodeID << " (type=" << static_cast<int>(srcNode->Type)
151 << "." << conn.SourcePinName << " -> node #" << conn.TargetNodeID << ")\n";
152 ++removedCount;
153 continue;
154 }
155
156 // Check 4: Destination node cannot be data-pure (data-pure nodes have no exec-in)
157 if (IsDataPureNode(dstNode->Type))
158 {
159 SYSTEM_LOG << "[TaskGraphTemplate] SanitizeExecConnections: Removing exec-in connection to data-pure node #"
160 << conn.TargetNodeID << " (type=" << static_cast<int>(dstNode->Type)
161 << " <- node #" << conn.SourceNodeID << "." << conn.SourcePinName << ")\n";
162 ++removedCount;
163 continue;
164 }
165
166 // Connection is valid, keep it
167 validConnections.push_back(conn);
168 }
169
170 // Replace with sanitized list
172
173 if (removedCount > 0)
174 {
175 SYSTEM_LOG << "[TaskGraphTemplate] SanitizeExecConnections: Removed " << removedCount
176 << " invalid exec connection(s) - graph is now clean\n";
177 }
178
179 return removedCount;
180}
181
182} // namespace Olympe
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Immutable asset structure shared by all task graph runners.
std::vector< TaskNodeDefinition > Nodes
All graph nodes.
std::vector< ExecPinConnection > ExecConnections
Explicit exec connections (ATS VS only)
std::string Name
Friendly name of this template (e.g. "PatrolBehaviour")
int32_t RootNodeID
ID of the root node (must exist in Nodes)
int SanitizeExecConnections()
Phase 24.3 - Poka-Yoke: Sanitizes exec connections to remove invalid links.
void BuildLookupCache()
Rebuilds the internal ID-to-node lookup map from the Nodes vector.
const TaskNodeDefinition * GetNode(int32_t nodeId) const
Returns a pointer to the node with the given ID, or nullptr.
bool Validate() const
Validates the structural integrity of the template.
std::unordered_map< int32_t, const TaskNodeDefinition * > m_nodeLookup
Fast lookup: node ID -> pointer into Nodes vector.
< Provides AssetID and INVALID_ASSET_ID
TaskNodeType
Identifies the role of a node in the task graph.
@ GetBBValue
Data node – reads a Blackboard key.
@ MathOp
Data node – arithmetic operation (+, -, *, /)
Explicit connection between a named exec-out pin of a source node and the exec-in pin of a target nod...
Full description of a single node in the task graph.
std::vector< int32_t > ChildrenIDs
Child node IDs (control-flow nodes only; empty for AtomicTask/Decorator leaf)
#define SYSTEM_LOG