Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
ParameterResolver.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
8ParameterResolver implementation: Resolves and merges prefab blueprint parameters
9with level instance parameters using priority-based merging.
10*/
11
12#include "ParameterResolver.h"
13#include "ParameterSchema.h"
14#include "system/system_utils.h"
15#include <algorithm>
16
18{
19 SYSTEM_LOG << "[ParameterResolver] Initialized" << std::endl;
20}
21
25
26std::vector<ResolvedComponentInstance> ParameterResolver::Resolve(
29{
30 SYSTEM_LOG << "[ParameterResolver] Resolving prefab '" << prefab.prefabName
31 << "' for instance '" << instanceParams.objectName << "'" << std::endl;
32
33 std::vector<ResolvedComponentInstance> resolvedComponents;
34
35 // Iterate through all components in the prefab
36 for (const auto& componentDef : prefab.components)
37 {
38 SYSTEM_LOG << "[ParameterResolver] Processing component: "
39 << componentDef.componentType << std::endl;
40
41 // Resolve each component with instance parameters
43
44 // Validate the resolved component
46
47 if (!resolved.isValid)
48 {
49 SYSTEM_LOG << "[ParameterResolver] WARNING: Component resolution failed for "
50 << resolved.componentType << std::endl;
51 for (const auto& error : resolved.errors)
52 {
53 SYSTEM_LOG << "[ParameterResolver] Error: " << error << std::endl;
54 }
55 }
56
58 }
59
60 SYSTEM_LOG << "[ParameterResolver] Resolution complete. Resolved "
61 << resolvedComponents.size() << " components" << std::endl;
62
63 return resolvedComponents;
64}
65
69{
71
72 // Step 1: Start with prefab defaults (LOWEST priority)
73 resolved.parameters = componentDef.parameters;
74
75 // Check if this component needs special position handling
76 bool isPositionComponent = (componentDef.componentType == "Position_data" ||
77 componentDef.componentType == "Position");
78
79 // Extract level overrides for this component
81
82 // Fast path: No overrides and not a position component - use prefab defaults directly
84 {
85 SYSTEM_LOG << "[ParameterResolver] ✓ Fast path: 0 overrides (using prefab defaults)" << std::endl;
86 return resolved;
87 }
88
89 SYSTEM_LOG << "[ParameterResolver] Starting with " << resolved.parameters.size()
90 << " prefab default parameters" << std::endl;
91
92 // Step 2: Apply position override (HIGHEST priority)
93 // Position is special - it's always from the level instance
95 {
96 SYSTEM_LOG << "[ParameterResolver] Applying level position override: ("
97 << instanceParams.position.x << ", "
98 << instanceParams.position.y << ", "
99 << instanceParams.position.z << ")" << std::endl;
100
101 resolved.parameters["x"] = ComponentParameter::FromFloat(instanceParams.position.x);
102 resolved.parameters["y"] = ComponentParameter::FromFloat(instanceParams.position.y);
103 resolved.parameters["z"] = ComponentParameter::FromFloat(instanceParams.position.z);
104 }
105
106 // Step 3: Apply level custom property overrides (HIGH priority)
107 if (!componentParams.empty())
108 {
109 SYSTEM_LOG << "[ParameterResolver] Applying " << componentParams.size()
110 << " level property overrides" << std::endl;
111
112 for (const auto& it : componentParams)
113 {
114 const std::string& propName = it.first;
115 const ComponentParameter& propValue = it.second;
117 }
118 }
119
120 return resolved;
121}
122
125 const std::string& propertyName,
127{
128 // Get schema to determine expected type
130 const ParameterSchemaEntry* schema = schemaRegistry.FindParameterSchema(propertyName);
131
132 if (schema != nullptr)
133 {
134 // Convert property value to expected type
136 propertyName,
138 schema->expectedType
139 );
140
141 // Override the parameter
142 component.parameters[propertyName] = converted;
143
144 SYSTEM_LOG << "[ParameterResolver] Override: " << propertyName
145 << " (type: " << static_cast<int>(converted.type) << ")" << std::endl;
146 }
147 else
148 {
149 // No schema found - apply as-is
150 component.parameters[propertyName] = propertyValue;
151
152 SYSTEM_LOG << "[ParameterResolver] Override (no schema): " << propertyName
153 << " (type: " << static_cast<int>(propertyValue.type) << ")" << std::endl;
154 }
155}
156
157std::map<std::string, ComponentParameter> ParameterResolver::ExtractComponentParameters(
158 const std::string& componentType,
160{
161 std::map<std::string, ComponentParameter> componentParams;
162
163 // PRIORITY 1: Check component-scoped overrides first (NEW)
164 // This prevents cross-component overwrites
165 auto compOverrideIt = instanceParams.componentOverrides.find(componentType);
166 if (compOverrideIt != instanceParams.componentOverrides.end())
167 {
168 // Copy all component-scoped parameters
170
171 #ifdef DEBUG_PARAMETER_RESOLUTION
172 SYSTEM_LOG << "[ParameterResolver] Found " << componentParams.size()
173 << " component-scoped overrides for " << componentType << std::endl;
174 #endif
175 }
176
177 // PRIORITY 2: Fall back to schema-based extraction from flat properties (LEGACY)
178 // This maintains backward compatibility with existing levels
180 const ComponentSchema* schema = schemaRegistry.GetComponentSchema(componentType);
181
182 if (schema == nullptr)
183 {
184 // If we found component overrides but no schema, that's fine - return what we have
185 if (!componentParams.empty())
186 {
187 #ifdef DEBUG_PARAMETER_RESOLUTION
188 SYSTEM_LOG << "[ParameterResolver] No schema for component " << componentType
189 << ", using component overrides only" << std::endl;
190 #endif
191 return componentParams;
192 }
193
194 SYSTEM_LOG << "[ParameterResolver] No schema found for component: "
195 << componentType << std::endl;
196 return componentParams;
197 }
198
199 // Extract parameters from flat properties that belong to this component
200 // Only add if not already present in componentParams (component overrides take precedence)
201 for (const auto& paramPair : schema->parameters)
202 {
203 const std::string& paramName = paramPair.first;
204
205 // Skip if already set via component overrides
206 if (componentParams.find(paramName) != componentParams.end())
207 {
208 continue;
209 }
210
211 // Check if level instance has this parameter in flat properties
212 auto it = instanceParams.properties.find(paramName);
213 if (it != instanceParams.properties.end())
214 {
215 componentParams[paramName] = it->second;
216 }
217 }
218
219 return componentParams;
220}
221
223 const std::string& propertyName,
225 ComponentParameter::Type expectedType)
226{
227 // If types match, return as-is
228 if (levelProperty.type == expectedType)
229 {
230 return levelProperty;
231 }
232
233 SYSTEM_LOG << "[ParameterResolver] Type conversion: " << propertyName
234 << " from type " << static_cast<int>(levelProperty.type)
235 << " to " << static_cast<int>(expectedType) << std::endl;
236
237 // Perform type conversion based on expected type
238 switch (expectedType)
239 {
242
245
248
251
254 {
255 Vector v = levelProperty.AsVector();
256 return ComponentParameter::FromVector3(v.x, v.y, v.z);
257 }
258
261 levelProperty.colorValue.r,
262 levelProperty.colorValue.g,
263 levelProperty.colorValue.b,
264 levelProperty.colorValue.a
265 );
266
269
270 default:
271 SYSTEM_LOG << "[ParameterResolver] WARNING: Unknown type conversion for "
272 << propertyName << std::endl;
273 return levelProperty;
274 }
275}
276
278{
279 // Get component schema for validation
281 const ComponentSchema* schema = schemaRegistry.GetComponentSchema(component.componentType);
282
283 // Fast path: Skip validation if no schema exists
284 if (schema == nullptr)
285 {
286 // No schema available - skip validation silently
287 return;
288 }
289
290 bool hasErrors = false;
291
292 // Check for missing required parameters
293 for (const auto& requiredParam : schema->requiredParams)
294 {
295 if (component.parameters.find(requiredParam) == component.parameters.end())
296 {
297 std::string error = "Missing required parameter: " + requiredParam;
298 component.errors.push_back(error);
299 component.isValid = false;
300 hasErrors = true;
301
302 SYSTEM_LOG << "[ParameterResolver] Validation ERROR: " << error << std::endl;
303 }
304 }
305
306 // Validate parameter types (only log actual mismatches)
307 for (const auto& paramPair : component.parameters)
308 {
309 const std::string& paramName = paramPair.first;
311
312 auto schemaIt = schema->parameters.find(paramName);
313 if (schemaIt != schema->parameters.end())
314 {
316
317 // Type validation
318 if (paramValue.type != schemaEntry.expectedType &&
320 {
321 std::string error = "Type mismatch for parameter '" + paramName +
322 "': expected " + std::to_string(static_cast<int>(schemaEntry.expectedType)) +
323 ", got " + std::to_string(static_cast<int>(paramValue.type));
324 component.errors.push_back(error);
325 hasErrors = true;
326
327 SYSTEM_LOG << "[ParameterResolver] Validation WARNING: " << error << std::endl;
328 }
329 }
330 }
331
332 // Only log success if there were no errors (reduces noise)
333 if (!hasErrors && component.isValid)
334 {
335 // Silently succeed - no need to log
336 }
337 else if (!component.isValid)
338 {
339 SYSTEM_LOG << "[ParameterResolver] Validation: Component "
340 << component.componentType << " has " << component.errors.size()
341 << " errors" << std::endl;
342 }
343}
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
std::map< std::string, ComponentParameter > ExtractComponentParameters(const std::string &componentType, const LevelInstanceParameters &instanceParams)
void ApplyPropertyOverride(ResolvedComponentInstance &component, const std::string &propertyName, const ComponentParameter &propertyValue)
ResolvedComponentInstance ResolveComponent(const ComponentDefinition &componentDef, const LevelInstanceParameters &instanceParams)
std::vector< ResolvedComponentInstance > Resolve(const PrefabBlueprint &prefab, const LevelInstanceParameters &instanceParams)
ComponentParameter ConvertLevelProperty(const std::string &propertyName, const ComponentParameter &levelProperty, ComponentParameter::Type expectedType)
void ValidateResolvedComponent(ResolvedComponentInstance &component)
static ParameterSchemaRegistry & GetInstance()
static ComponentParameter FromBool(bool value)
static ComponentParameter FromFloat(float value)
static ComponentParameter FromEntityRef(EntityID entityId)
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)
#define SYSTEM_LOG