Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
ParameterSchema.cpp
Go to the documentation of this file.
1/*
2Olympe Engine V2 - 2025
3Nicolas Chereau
4nchereau@gmail.com
5
6This file is part of Olympe Engine V2.
7
8ParameterSchema purpose: Implementation of the parameter schema registry.
9*/
10
11#include "ParameterSchema.h"
12#include "PrefabScanner.h"
13#include "system/system_utils.h"
14#include "third_party/nlohmann/json.hpp"
15#include <algorithm>
16#include <fstream>
17
18// Auto-initialization
20{
21 if (!isInitialized_)
22 {
24 isInitialized_ = true;
25 SYSTEM_LOG << "ParameterSchemaRegistry auto-initialized" << std::endl;
26 }
27}
28
30{
31 SYSTEM_LOG << "Initializing built-in parameter schemas..." << std::endl;
32
33 // Position_data component schemas
35 "position", "Position_data", "position",
37 ComponentParameter::FromVector3(0.0f, 0.0f, 0.0f)
38 ));
39
40 // Register aliases for position
41 aliasToParameter_["pos"] = "position";
42 aliasToParameter_["location"] = "position";
43
44 // PhysicsBody_data component schemas
46 "speed", "PhysicsBody_data", "speed",
49 ));
50
52 "mass", "PhysicsBody_data", "mass",
55 ));
56
58 "friction", "PhysicsBody_data", "friction",
61 ));
62
64 "useGravity", "PhysicsBody_data", "useGravity",
67 ));
68
69 // AIBlackboard_data component schemas
71 "alertRadius", "AIBlackboard_data", "alertRadius",
74 ));
75
77 "health", "AIBlackboard_data", "health",
80 ));
81
82 // Note: patrolPoints and patrolRoute are different fields for AI navigation
84 "patrolPoints", "AIBlackboard_data", "patrolPoints",
87 ));
88
90 "initialized", "AIBlackboard_data", "initialized",
93 ));
94
96 "patrolRoute", "AIBlackboard_data", "patrolRoute",
99 ));
100
101 // VisualSprite_data component schemas
103 "spritePath", "VisualSprite_data", "spritePath",
106 ));
107
109 "hotSpot", "VisualSprite_data", "hotSpot",
112 ));
113
115 "color", "VisualSprite_data", "color",
117 ComponentParameter::FromColor(255, 255, 255, 255)
118 ));
119
120 // Note: width, height, layer are also used in VisualEditor_data component
122 "width", "VisualSprite_data", "width",
125 ));
126
128 "height", "VisualSprite_data", "height",
131 ));
132
134 "layer", "VisualSprite_data", "layer",
137 ));
138
139 // Identity_data component schemas
141 "name", "Identity_data", "name",
144 ));
145
147 "type", "Identity_data", "type",
150 ));
151
152 // "category" is an alias for "tag" field (backward compatibility)
153 // If both "category" and "tag" are specified, the last one processed will take precedence
155 "category", "Identity_data", "tag",
158 ));
159
160 // "tag" parameter maps to "tag" field (preferred parameter name)
162 "tag", "Identity_data", "tag",
165 ));
166
168 "entityType", "Identity_data", "entityType",
171 ));
172
173 // BoundingBox_data component schemas
175 "width", "BoundingBox_data", "width",
178 ));
179
181 "height", "BoundingBox_data", "height",
184 ));
185
187 "offsetX", "BoundingBox_data", "offsetX",
190 ));
191
193 "offsetY", "BoundingBox_data", "offsetY",
196 ));
197
198 // Movement_data component schemas
200 "speed", "Movement_data", "speed",
203 ));
204
206 "acceleration", "Movement_data", "acceleration",
209 ));
210
211 // Health_data component schemas
213 "maxHealth", "Health_data", "maxHealth",
216 ));
217
219 "currentHealth", "Health_data", "currentHealth",
222 ));
223
225 "invulnerable", "Health_data", "invulnerable",
228 ));
229
230 // VisualEditor_data component schemas
231 // Note: Uses spritePath (like VisualSprite_data) but different purpose (editor visualization)
233 "spritePath", "VisualEditor_data", "spritePath",
236 ));
237
238 // Note: width, height, layer are separate registrations for VisualEditor_data component
240 "width", "VisualEditor_data", "width",
243 ));
244
246 "height", "VisualEditor_data", "height",
249 ));
250
252 "layer", "VisualEditor_data", "layer",
255 ));
256
257 // AIState_data component schemas
259 "currentState", "AIState_data", "currentState",
262 ));
263
265 "previousState", "AIState_data", "previousState",
268 ));
269
270 // AISenses_data component schemas
272 "visionRange", "AISenses_data", "visionRange",
275 ));
276
278 "hearingRange", "AISenses_data", "hearingRange",
281 ));
282
284 "alertLevel", "AISenses_data", "alertLevel",
287 ));
288
289 // BehaviorTreeRuntime_data component schemas
291 "AITreePath", "BehaviorTreeRuntime_data", "AITreePath",
294 ));
295
297 "active", "BehaviorTreeRuntime_data", "active",
300 ));
301
302 // MoveIntent_data component schemas
304 "targetX", "MoveIntent_data", "targetX",
307 ));
308
310 "targetY", "MoveIntent_data", "targetY",
313 ));
314
316 "hasTarget", "MoveIntent_data", "hasTarget",
319 ));
320
321 // AttackIntent_data component schemas
323 "damage", "AttackIntent_data", "damage",
326 ));
327
329 "attackRange", "AttackIntent_data", "attackRange",
332 ));
333
335 "cooldown", "AttackIntent_data", "cooldown",
338 ));
339
340 // PlayerBinding_data component schemas
342 "playerIndex", "PlayerBinding_data", "playerIndex",
345 ));
346
348 "controlScheme", "PlayerBinding_data", "controlScheme",
351 ));
352
353 // Controller_data component schemas
355 "enabled", "Controller_data", "enabled",
358 ));
359
361 "inputEnabled", "Controller_data", "inputEnabled",
364 ));
365
366 SYSTEM_LOG << "Built-in parameter schemas initialized: "
367 << parameterToComponent_.size() << " parameters registered." << std::endl;
368}
369
370bool ParameterSchemaRegistry::LoadSchemaFromFile(const std::string& filepath)
371{
372 // Legacy method - redirect to new implementation
373 return LoadFromJSON(filepath);
374}
375
376bool ParameterSchemaRegistry::LoadFromJSON(const std::string& filepath)
377{
378 std::ifstream file(filepath);
379 if (!file.is_open())
380 {
381 return false;
382 }
383
385 try
386 {
387 file >> root;
388 file.close();
389 }
390 catch (const std::exception& e)
391 {
392 SYSTEM_LOG << " x JSON parse error: " << e.what() << std::endl;
393 return false;
394 }
395
396 if (!root.contains("schemas") || !root["schemas"].is_array())
397 {
398 return false;
399 }
400
401 // Clear existing schemas (but keep any auto-discovered ones)
402 // Note: We don't clear completely to allow discovered schemas to coexist
403
404 int loadedSchemas = 0;
405
406 for (const auto& schemaJson : root["schemas"])
407 {
408 if (!schemaJson.contains("componentType") || !schemaJson.contains("parameters"))
409 {
410 continue;
411 }
412
413 std::string componentType = schemaJson["componentType"].get<std::string>();
414
415 if (!schemaJson["parameters"].is_array())
416 {
417 continue;
418 }
419
420 for (const auto& paramJson : schemaJson["parameters"])
421 {
422 if (!paramJson.contains("name") || !paramJson.contains("type"))
423 {
424 continue;
425 }
426
427 std::string paramName = paramJson["name"].get<std::string>();
428 std::string typeStr = paramJson["type"].get<std::string>();
429
431
432 // Parse default value based on type
433 ComponentParameter defaultValue;
434 if (paramJson.contains("defaultValue"))
435 {
436 defaultValue = ParseDefaultValue(paramJson["defaultValue"], paramType);
437 }
438 else
439 {
440 // Create empty default based on type
441 switch (paramType)
442 {
444 defaultValue = ComponentParameter::FromBool(false);
445 break;
447 defaultValue = ComponentParameter::FromInt(0);
448 break;
450 defaultValue = ComponentParameter::FromFloat(0.0f);
451 break;
453 defaultValue = ComponentParameter::FromString("");
454 break;
455 default:
456 defaultValue = ComponentParameter();
457 break;
458 }
459 }
460
461 // Register the schema entry
463 paramName,
464 componentType,
465 paramName, // field name = param name by default
466 paramType,
467 false, // not required by default
468 defaultValue
469 );
470
473 }
474 }
475
476 SYSTEM_LOG << " ✓ Loaded " << loadedSchemas << " parameter schemas from JSON" << std::endl;
477
478 return true;
479}
480
482{
483 if (typeStr == "Bool") return ComponentParameter::Type::Bool;
484 if (typeStr == "Int") return ComponentParameter::Type::Int;
485 if (typeStr == "Float") return ComponentParameter::Type::Float;
486 if (typeStr == "String") return ComponentParameter::Type::String;
487 if (typeStr == "Vector2") return ComponentParameter::Type::Vector2;
488 if (typeStr == "Vector3") return ComponentParameter::Type::Vector3;
489 if (typeStr == "Color") return ComponentParameter::Type::Color;
490 if (typeStr == "Array") return ComponentParameter::Type::Array;
491 if (typeStr == "EntityRef") return ComponentParameter::Type::EntityRef;
493}
494
496{
497 switch (type)
498 {
500 return ComponentParameter::FromBool(valueJson.get<bool>());
501
503 return ComponentParameter::FromInt(valueJson.get<int>());
504
506 return ComponentParameter::FromFloat(valueJson.get<float>());
507
509 return ComponentParameter::FromString(valueJson.get<std::string>());
510
513 {
514 if (valueJson.is_object())
515 {
516 float x = valueJson.value("x", 0.0f);
517 float y = valueJson.value("y", 0.0f);
518 float z = valueJson.value("z", 0.0f);
519 return ComponentParameter::FromVector3(x, y, z);
520 }
521 return ComponentParameter::FromVector3(0.0f, 0.0f, 0.0f);
522 }
523
525 {
526 if (valueJson.is_string())
527 {
528 std::string colorStr = valueJson.get<std::string>();
529 // Simple hex color parsing (#RRGGBB or #RRGGBBAA)
530 if (colorStr.length() >= 7 && colorStr[0] == '#')
531 {
532 int r = std::stoi(colorStr.substr(1, 2), nullptr, 16);
533 int g = std::stoi(colorStr.substr(3, 2), nullptr, 16);
534 int b = std::stoi(colorStr.substr(5, 2), nullptr, 16);
535 int a = (colorStr.length() >= 9) ? std::stoi(colorStr.substr(7, 2), nullptr, 16) : 255;
536 return ComponentParameter::FromColor(r, g, b, a);
537 }
538 }
539 return ComponentParameter::FromColor(255, 255, 255, 255);
540 }
541
543 {
544 // For arrays, we store the JSON array as a string for now
545 // More sophisticated handling could be added later
547 }
548
549 default:
550 return ComponentParameter();
551 }
552}
553
555{
556 return parameterToComponent_.size();
557}
558
559const ParameterSchemaEntry* ParameterSchemaRegistry::FindParameterSchema(const std::string& parameterName) const
560{
561 // Check if this is an alias first
562 std::string actualParamName = parameterName;
563 auto aliasIt = aliasToParameter_.find(parameterName);
564 if (aliasIt != aliasToParameter_.end())
565 {
566 actualParamName = aliasIt->second;
567 }
568
569 // Find which component this parameter belongs to
571 if (it == parameterToComponent_.end())
572 {
573 return nullptr;
574 }
575
576 const std::string& componentType = it->second;
577
578 // Get the component schema
579 auto schemaIt = componentSchemas_.find(componentType);
580 if (schemaIt == componentSchemas_.end())
581 {
582 return nullptr;
583 }
584
585 // Find the parameter within the component schema
586 const ComponentSchema& schema = schemaIt->second;
588 if (paramIt == schema.parameters.end())
589 {
590 return nullptr;
591 }
592
593 return &(paramIt->second);
594}
595
596const ComponentSchema* ParameterSchemaRegistry::GetComponentSchema(const std::string& componentType) const
597{
598 auto it = componentSchemas_.find(componentType);
599 if (it == componentSchemas_.end())
600 {
601 return nullptr;
602 }
603 return &(it->second);
604}
605
606bool ParameterSchemaRegistry::ValidateParameter(const std::string& parameterName, const ComponentParameter& param) const
607{
608 const ParameterSchemaEntry* entry = FindParameterSchema(parameterName);
609 if (!entry)
610 {
611 SYSTEM_LOG << "Warning: No schema found for parameter '" << parameterName << "'" << std::endl;
612 return false;
613 }
614
615 // Check type compatibility
616 if (entry->expectedType != param.type && param.type != ComponentParameter::Type::Unknown)
617 {
618 // Allow some type conversions
619 bool allowConversion = false;
620
621 // Int -> Float conversion
623 {
624 allowConversion = true;
625 }
626 // Float -> Int conversion (with warning)
628 {
629 allowConversion = true;
630 SYSTEM_LOG << "Warning: Converting float to int for parameter '" << parameterName << "'" << std::endl;
631 }
632 // Vector2 -> Vector3 conversion
634 {
635 allowConversion = true;
636 }
637
638 if (!allowConversion)
639 {
640 SYSTEM_LOG << "Error: Type mismatch for parameter '" << parameterName
641 << "'. Expected type " << static_cast<int>(entry->expectedType)
642 << " but got " << static_cast<int>(param.type) << std::endl;
643 return false;
644 }
645 }
646
647 return true;
648}
649
651{
652 // Get or create component schema
653 ComponentSchema& schema = componentSchemas_[entry.targetComponent];
654 if (schema.componentType.empty())
655 {
656 schema.componentType = entry.targetComponent;
657 }
658
659 // Add parameter to schema
660 schema.parameters[entry.parameterName] = entry;
661
662 // Track required parameters
663 if (entry.isRequired)
664 {
665 schema.requiredParams.insert(entry.parameterName);
666 }
667
668 // Add to quick lookup map
669 parameterToComponent_[entry.parameterName] = entry.targetComponent;
670}
671
672// Automatic schema discovery methods
674{
675 // Check if component already has a complete schema
676 auto it = componentSchemas_.find(componentDef.componentType);
677
678 // For each parameter in the component definition
679 for (const auto& paramPair : componentDef.parameters)
680 {
681 const std::string& paramName = paramPair.first;
683
684 // Skip if already registered
685 if (it != componentSchemas_.end() &&
686 it->second.parameters.find(paramName) != it->second.parameters.end())
687 {
688 continue;
689 }
690
691 // Auto-discover and register
693 componentDef.componentType,
694 paramName,
695 paramValue.type,
697 );
698
699 SYSTEM_LOG << "[SchemaDiscovery] Auto-registered: "
700 << componentDef.componentType << "." << paramName
701 << " (type: " << static_cast<int>(paramValue.type) << ")"
702 << std::endl;
703 }
704}
705
713
715 const std::string& componentType,
716 const std::string& paramName,
718 const ComponentParameter& defaultValue)
719{
721 paramName,
722 componentType,
723 paramName, // Field name defaults to parameter name (can differ in manual registrations)
724 paramType,
725 false, // Not required by default
726 defaultValue
727 );
728
730}
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
ComponentParameter ParseDefaultValue(const nlohmann::json &valueJson, ComponentParameter::Type type) const
const ParameterSchemaEntry * FindParameterSchema(const std::string &parameterName) const
void RegisterParameterSchema(const ParameterSchemaEntry &entry)
bool LoadSchemaFromFile(const std::string &filepath)
void DiscoverComponentSchema(const ComponentDefinition &componentDef)
bool ValidateParameter(const std::string &parameterName, const ComponentParameter &param) const
bool LoadFromJSON(const std::string &filepath)
const ComponentSchema * GetComponentSchema(const std::string &componentType) const
void DiscoverSchemasFromPrefab(const PrefabBlueprint &prefab)
std::map< std::string, ComponentSchema > componentSchemas_
std::map< std::string, std::string > aliasToParameter_
void AutoRegisterParameter(const std::string &componentType, const std::string &paramName, ComponentParameter::Type paramType, const ComponentParameter &defaultValue)
ComponentParameter::Type StringToParameterType(const std::string &typeStr) const
std::map< std::string, std::string > parameterToComponent_
nlohmann::json json
static ComponentParameter FromBool(bool value)
static ComponentParameter FromFloat(float value)
static ComponentParameter FromVector2(float x, float y)
static ComponentParameter FromVector3(float x, float y, float z)
static ComponentParameter FromColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a=255)
static ComponentParameter FromInt(int value)
static ComponentParameter FromString(const std::string &value)
std::string componentType
std::map< std::string, ParameterSchemaEntry > parameters
#define SYSTEM_LOG