Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
LocalBlackboard.cpp
Go to the documentation of this file.
1/**
2 * @file LocalBlackboard.cpp
3 * @brief Implementation of LocalBlackboard for the Atomic Task System
4 * @author Olympe Engine
5 * @date 2026-02-20
6 */
7
8#include "LocalBlackboard.h"
9#include "TaskGraphTemplate.h"
10#include "../NodeGraphCore/GlobalBlackboard.h"
11
12#include <stdexcept>
13#include <string>
14#include <cstring>
15
16#include "../system/system_utils.h"
17
18namespace Olympe {
19
20// ============================================================================
21// Constructor
22// ============================================================================
23
27
28// ============================================================================
29// Lifecycle
30// ============================================================================
31
33{
34 m_variables.clear();
35 m_defaults.clear();
36 m_types.clear();
37
38 for (size_t i = 0; i < tmpl.LocalVariables.size(); ++i)
39 {
40 const VariableDefinition& def = tmpl.LocalVariables[i];
41 m_variables[def.Name] = def.DefaultValue;
42 m_defaults[def.Name] = def.DefaultValue;
43 m_types[def.Name] = def.Type;
44 }
45
46 SYSTEM_LOG << "[LocalBlackboard] Initialized with " << tmpl.LocalVariables.size()
47 << " variables from template '" << tmpl.Name << "'" << std::endl;
48}
49
51{
52 for (auto it = m_defaults.begin(); it != m_defaults.end(); ++it)
53 {
54 m_variables[it->first] = it->second;
55 }
56
57 SYSTEM_LOG << "[LocalBlackboard] Reset to defaults" << std::endl;
58}
59
60// ============================================================================
61// Variable access
62// ============================================================================
63
65{
66 auto it = m_variables.find(varName);
67 if (it == m_variables.end())
68 {
69 throw std::runtime_error("[LocalBlackboard] Unknown variable: " + varName);
70 }
71 return it->second;
72}
73
74void LocalBlackboard::SetValue(const std::string& varName, const TaskValue& value)
75{
76 auto typeIt = m_types.find(varName);
77 if (typeIt == m_types.end())
78 {
79 throw std::runtime_error("[LocalBlackboard] Unknown variable: " + varName);
80 }
81
82 if (value.GetType() != typeIt->second)
83 {
84 throw std::runtime_error("[LocalBlackboard] Type mismatch for variable: " + varName);
85 }
86
87 m_variables[varName] = value;
88}
89
90// ============================================================================
91// Queries
92// ============================================================================
93
94bool LocalBlackboard::HasVariable(const std::string& varName) const
95{
96 return m_variables.find(varName) != m_variables.end();
97}
98
99std::vector<std::string> LocalBlackboard::GetVariableNames() const
100{
101 std::vector<std::string> names;
102 names.reserve(m_variables.size());
103
104 for (auto it = m_variables.begin(); it != m_variables.end(); ++it)
105 {
106 names.push_back(it->first);
107 }
108
109 return names;
110}
111
112// ============================================================================
113// Persistence helpers
114// ============================================================================
115
116namespace {
117
118template<typename T>
119static void WriteBytes(std::vector<uint8_t>& buf, const T& value)
120{
121 const uint8_t* p = reinterpret_cast<const uint8_t*>(&value);
122 buf.insert(buf.end(), p, p + sizeof(T));
123}
124
125static bool ReadBytes(const std::vector<uint8_t>& buf, size_t& pos, void* dst, size_t n)
126{
127 if (pos + n > buf.size()) return false;
128 std::memcpy(dst, buf.data() + pos, n);
129 pos += n;
130 return true;
131}
132
133} // anonymous namespace
134
135// ============================================================================
136// Serialize
137// ============================================================================
138
139void LocalBlackboard::Serialize(std::vector<uint8_t>& outBytes) const
140{
141 outBytes.clear();
142
143 uint32_t count = static_cast<uint32_t>(m_variables.size());
144 WriteBytes(outBytes, count);
145
146 for (auto it = m_variables.begin(); it != m_variables.end(); ++it)
147 {
148 const std::string& name = it->first;
149 const TaskValue& val = it->second;
150
151 uint32_t nameLen = static_cast<uint32_t>(name.size());
152 WriteBytes(outBytes, nameLen);
153 outBytes.insert(outBytes.end(), name.begin(), name.end());
154
155 uint8_t type = static_cast<uint8_t>(val.GetType());
156 WriteBytes(outBytes, type);
157
158 switch (val.GetType())
159 {
161 {
162 uint8_t bv = val.AsBool() ? 1u : 0u;
163 WriteBytes(outBytes, bv);
164 break;
165 }
167 {
168 int32_t iv = static_cast<int32_t>(val.AsInt());
169 WriteBytes(outBytes, iv);
170 break;
171 }
173 {
174 float fv = val.AsFloat();
175 WriteBytes(outBytes, fv);
176 break;
177 }
179 {
180 ::Vector vv = val.AsVector();
181 WriteBytes(outBytes, vv.x);
182 WriteBytes(outBytes, vv.y);
183 WriteBytes(outBytes, vv.z);
184 break;
185 }
187 {
188 uint64_t eid = static_cast<uint64_t>(val.AsEntityID());
189 WriteBytes(outBytes, eid);
190 break;
191 }
193 {
194 const std::string& sv = val.AsString();
195 uint32_t slen = static_cast<uint32_t>(sv.size());
196 WriteBytes(outBytes, slen);
197 outBytes.insert(outBytes.end(), sv.begin(), sv.end());
198 break;
199 }
200 default:
201 break;
202 }
203 }
204
205 SYSTEM_LOG << "[LocalBlackboard] Serialized " << count << " variables ("
206 << outBytes.size() << " bytes)\n";
207}
208
209// ============================================================================
210// Deserialize
211// ============================================================================
212
213void LocalBlackboard::Deserialize(const std::vector<uint8_t>& inBytes)
214{
215 size_t pos = 0;
216
217 uint32_t count = 0;
218 if (!ReadBytes(inBytes, pos, &count, sizeof(count)))
219 {
220 SYSTEM_LOG << "[LocalBlackboard] Deserialize: buffer too short for count\n";
221 return;
222 }
223
224 for (uint32_t i = 0; i < count; ++i)
225 {
226 // --- name ---
227 uint32_t nameLen = 0;
228 if (!ReadBytes(inBytes, pos, &nameLen, sizeof(nameLen)))
229 {
230 SYSTEM_LOG << "[LocalBlackboard] Deserialize: buffer truncated at entry " << i << "\n";
231 return;
232 }
233 if (pos + nameLen > inBytes.size())
234 {
235 SYSTEM_LOG << "[LocalBlackboard] Deserialize: name bytes truncated at entry " << i << "\n";
236 return;
237 }
238 std::string name(reinterpret_cast<const char*>(inBytes.data() + pos), nameLen);
239 pos += nameLen;
240
241 // --- type tag ---
242 uint8_t typeTag = 0;
243 if (!ReadBytes(inBytes, pos, &typeTag, sizeof(typeTag)))
244 {
245 SYSTEM_LOG << "[LocalBlackboard] Deserialize: buffer truncated at type for '" << name << "'\n";
246 return;
247 }
249
250 // --- value ---
251 bool parseOk = true;
253
254 switch (storedType)
255 {
257 {
258 uint8_t bv = 0;
259 parseOk = ReadBytes(inBytes, pos, &bv, sizeof(bv));
260 if (parseOk) val = TaskValue(bv != 0);
261 break;
262 }
264 {
265 int32_t iv = 0;
266 parseOk = ReadBytes(inBytes, pos, &iv, sizeof(iv));
267 if (parseOk) val = TaskValue(static_cast<int>(iv));
268 break;
269 }
271 {
272 float fv = 0.0f;
273 parseOk = ReadBytes(inBytes, pos, &fv, sizeof(fv));
274 if (parseOk) val = TaskValue(fv);
275 break;
276 }
278 {
279 float x = 0.0f, y = 0.0f, z = 0.0f;
280 parseOk = ReadBytes(inBytes, pos, &x, sizeof(x))
281 && ReadBytes(inBytes, pos, &y, sizeof(y))
282 && ReadBytes(inBytes, pos, &z, sizeof(z));
283 if (parseOk) val = TaskValue(::Vector(x, y, z));
284 break;
285 }
287 {
288 uint64_t eid = 0;
289 parseOk = ReadBytes(inBytes, pos, &eid, sizeof(eid));
290 if (parseOk) val = TaskValue(static_cast<EntityID>(eid));
291 break;
292 }
294 {
295 uint32_t slen = 0;
296 parseOk = ReadBytes(inBytes, pos, &slen, sizeof(slen));
297 if (parseOk)
298 {
299 if (pos + slen > inBytes.size()) { parseOk = false; break; }
300 std::string sv(reinterpret_cast<const char*>(inBytes.data() + pos), slen);
301 pos += slen;
302 val = TaskValue(sv);
303 }
304 break;
305 }
306 default:
307 SYSTEM_LOG << "[LocalBlackboard] Deserialize: unknown type tag "
308 << static_cast<int>(typeTag) << " for '" << name << "' - aborting\n";
309 return;
310 }
311
312 if (!parseOk)
313 {
314 SYSTEM_LOG << "[LocalBlackboard] Deserialize: buffer truncated while reading value for '"
315 << name << "'\n";
316 return;
317 }
318
319 // --- schema check ---
320 auto typeIt = m_types.find(name);
321 if (typeIt == m_types.end())
322 {
323 SYSTEM_LOG << "[LocalBlackboard] Deserialize: unknown variable '" << name << "' - skipping\n";
324 continue;
325 }
326 if (typeIt->second != storedType)
327 {
328 SYSTEM_LOG << "[LocalBlackboard] Deserialize: type mismatch for '" << name << "' - skipping\n";
329 continue;
330 }
331
332 m_variables[name] = val;
333 }
334
335 SYSTEM_LOG << "[LocalBlackboard] Deserialized " << count << " entries\n";
336}
337
338// ============================================================================
339// InitializeFromEntries (ATS VS Phase 2)
340// ============================================================================
341
342void LocalBlackboard::InitializeFromEntries(const std::vector<BlackboardEntry>& entries)
343{
344 m_variables.clear();
345 m_defaults.clear();
346 m_types.clear();
347
348 for (size_t i = 0; i < entries.size(); ++i)
349 {
350 const BlackboardEntry& entry = entries[i];
351
352 // Skip global entries — they are not stored in the local blackboard.
353 if (entry.IsGlobal)
354 {
355 continue;
356 }
357
359 m_defaults[entry.Key] = entry.Default;
360 m_types[entry.Key] = entry.Type;
361 }
362
363 SYSTEM_LOG << "[LocalBlackboard] InitializeFromEntries: registered "
364 << m_variables.size() << " local variables\n";
365}
366
367// ============================================================================
368// SetValueScoped (ATS VS Phase 2)
369// ============================================================================
370
371void LocalBlackboard::SetValueScoped(const std::string& scopedKey, const TaskValue& value)
372{
373 static const std::string localPrefix = "local:";
374 static const std::string globalPrefix = "global:";
375
376 if (scopedKey.compare(0, localPrefix.size(), localPrefix) == 0)
377 {
378 const std::string key = scopedKey.substr(localPrefix.size());
379 try
380 {
381 SetValue(key, value);
382 }
383 catch (const std::exception& e)
384 {
385 SYSTEM_LOG << "[LocalBlackboard] SetValueScoped: " << e.what() << "\n";
386 }
387 }
388 else if (scopedKey.compare(0, globalPrefix.size(), globalPrefix) == 0)
389 {
390 const std::string key = scopedKey.substr(globalPrefix.size());
392 }
393 else
394 {
395 // No prefix — treat as local key for legacy compatibility.
396 try
397 {
398 SetValue(scopedKey, value);
399 }
400 catch (const std::exception& e)
401 {
402 SYSTEM_LOG << "[LocalBlackboard] SetValueScoped: " << e.what() << "\n";
403 }
404 }
405}
406
407// ============================================================================
408// GetValueScoped (ATS VS Phase 2)
409// ============================================================================
410
412{
413 static const std::string localPrefix = "local:";
414 static const std::string globalPrefix = "global:";
415
416 if (scopedKey.compare(0, globalPrefix.size(), globalPrefix) == 0)
417 {
418 const std::string key = scopedKey.substr(globalPrefix.size());
420 }
421
422 if (scopedKey.compare(0, localPrefix.size(), localPrefix) == 0)
423 {
424 const std::string key = scopedKey.substr(localPrefix.size());
425 try
426 {
427 return GetValue(key);
428 }
429 catch (...)
430 {
431 return TaskValue();
432 }
433 }
434
435 // No prefix — treat as local key for legacy compatibility.
436 try
437 {
438 return GetValue(scopedKey);
439 }
440 catch (...)
441 {
442 return TaskValue();
443 }
444}
445
446} // namespace Olympe
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
std::uint64_t EntityID
Definition ECS_Entity.h:21
Runtime key-value store for task graph variables.
Immutable asset structure shared by all task graph runners.
void SetVar(const std::string &key, const TaskValue &value)
Writes or creates a variable.
static GlobalBlackboard & Get()
Returns the singleton instance.
TaskValue GetVar(const std::string &key) const
Reads a variable by key.
void Serialize(std::vector< uint8_t > &outBytes) const
Serializes all variable names and typed values into a byte buffer.
std::unordered_map< std::string, VariableType > m_types
Declared type of each variable (used for type validation in SetValue).
TaskValue GetValueScoped(const std::string &scopedKey) const
Gets a value using a scoped key (prefix "local:" is stripped).
void Deserialize(const std::vector< uint8_t > &inBytes)
Restores variable values from a byte buffer produced by Serialize().
void Reset()
Resets all variables to their default values.
void Initialize(const TaskGraphTemplate &tmpl)
Initialises the blackboard from a template.
std::unordered_map< std::string, TaskValue > m_defaults
Default (initial) values used by Reset().
bool HasVariable(const std::string &varName) const
Returns true if a variable with the given name is registered.
void SetValue(const std::string &varName, const TaskValue &value)
Sets the value of a variable.
TaskValue GetValue(const std::string &varName) const
Returns the current value of a variable.
void SetValueScoped(const std::string &scopedKey, const TaskValue &value)
Sets a value using a scoped key (prefix "local:" is stripped).
std::unordered_map< std::string, TaskValue > m_variables
Current values for each registered variable.
std::vector< std::string > GetVariableNames() const
Returns all registered variable names (useful for debugging / editor).
LocalBlackboard()
Default constructor.
void InitializeFromEntries(const std::vector< BlackboardEntry > &entries)
Initializes the blackboard from a vector of BlackboardEntry (ATS VS schema v4).
Immutable, shareable task graph asset.
C++14-compliant type-safe value container for task parameters.
VariableType GetType() const
Returns the VariableType tag of the stored value.
static void WriteBytes(std::vector< uint8_t > &buf, const T &value)
static bool ReadBytes(const std::vector< uint8_t > &buf, size_t &pos, void *dst, size_t n)
< Provides AssetID and INVALID_ASSET_ID
VariableType
Type tags used by TaskValue to identify stored data.
@ Int
32-bit signed integer
@ Float
Single-precision float.
@ String
std::string
@ Vector
3-component vector (Vector from vector.h)
@ EntityID
Entity identifier (uint64_t)
Single entry in the graph's declared blackboard schema (local or global).
Declares a single variable in the task graph's blackboard schema.
std::string Name
Variable name (must be unique within the template)
VariableType Type
Declared type.
TaskValue DefaultValue
Initial value (used by LocalBlackboard::Reset)
#define SYSTEM_LOG