!I (1670414, 1670415, 1670416, 1670424, 1670431):
[CRYENGINE.git] / Code / CryPlugins / CryUQS / Core / core / QueryBlueprint.cpp
blob79f06b4df81a6c567f415aabd5460bde5303e998
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "StdAfx.h"
4 #include "QueryBlueprint.h"
6 // *INDENT-OFF* - <hard to read code and declarations due to inconsistent indentation>
8 namespace UQS
10 namespace Core
13 //===================================================================================
15 // CTextualQueryBlueprint
17 //===================================================================================
19 CTextualQueryBlueprint::CTextualQueryBlueprint()
20 : m_queryFactoryGUID(CryGUID::Null())
21 , m_maxItemsToKeepInResultSet(1) // default to 1 item in the result set (i.e. the most common use-case), instead of 0 (which would mean: "return me as many items as possible")
23 // nothing
26 CTextualQueryBlueprint::~CTextualQueryBlueprint()
28 for (CTextualEvaluatorBlueprint* pIE : m_instantEvaluators)
30 delete pIE;
33 for (CTextualEvaluatorBlueprint* pDE : m_deferredEvaluators)
35 delete pDE;
38 for (CTextualQueryBlueprint* pChild : m_children)
40 delete pChild;
44 void CTextualQueryBlueprint::SetName(const char* szName)
46 m_name = szName;
49 void CTextualQueryBlueprint::SetQueryFactoryName(const char* szFactoryName)
51 m_queryFactoryName = szFactoryName;
54 void CTextualQueryBlueprint::SetQueryFactoryGUID(const CryGUID& factoryGUID)
56 m_queryFactoryGUID = factoryGUID;
59 void CTextualQueryBlueprint::SetMaxItemsToKeepInResultSet(size_t maxItems)
61 m_maxItemsToKeepInResultSet = maxItems;
64 ITextualGlobalConstantParamsBlueprint& CTextualQueryBlueprint::GetGlobalConstantParams()
66 return m_globalConstantParams;
69 ITextualGlobalRuntimeParamsBlueprint& CTextualQueryBlueprint::GetGlobalRuntimeParams()
71 return m_globalRuntimeParams;
74 ITextualGeneratorBlueprint& CTextualQueryBlueprint::SetGenerator()
76 m_pGenerator.reset(new CTextualGeneratorBlueprint);
77 return *m_pGenerator;
80 ITextualEvaluatorBlueprint& CTextualQueryBlueprint::AddInstantEvaluator()
82 CTextualEvaluatorBlueprint* pNewInstantEvaluatorBP = new CTextualEvaluatorBlueprint;
83 m_instantEvaluators.push_back(pNewInstantEvaluatorBP);
84 return *pNewInstantEvaluatorBP;
87 size_t CTextualQueryBlueprint::GetInstantEvaluatorCount() const
89 return m_instantEvaluators.size();
92 ITextualEvaluatorBlueprint& CTextualQueryBlueprint::AddDeferredEvaluator()
94 CTextualEvaluatorBlueprint* pNewDeferredEvaluatorBlueprint = new CTextualEvaluatorBlueprint;
95 m_deferredEvaluators.push_back(pNewDeferredEvaluatorBlueprint);
96 return *pNewDeferredEvaluatorBlueprint;
99 ITextualQueryBlueprint& CTextualQueryBlueprint::AddChild()
101 CTextualQueryBlueprint* pNewChild = new CTextualQueryBlueprint;
102 m_children.push_back(pNewChild);
103 return *pNewChild;
106 size_t CTextualQueryBlueprint::GetDeferredEvaluatorCount() const
108 return m_deferredEvaluators.size();
111 const char* CTextualQueryBlueprint::GetName() const
113 return m_name.c_str();
116 const char* CTextualQueryBlueprint::GetQueryFactoryName() const
118 return m_queryFactoryName.c_str();
121 const CryGUID& CTextualQueryBlueprint::GetQueryFactoryGUID() const
123 return m_queryFactoryGUID;
126 size_t CTextualQueryBlueprint::GetMaxItemsToKeepInResultSet() const
128 return m_maxItemsToKeepInResultSet;
131 const ITextualGlobalConstantParamsBlueprint& CTextualQueryBlueprint::GetGlobalConstantParams() const
133 return m_globalConstantParams;
136 const ITextualGlobalRuntimeParamsBlueprint& CTextualQueryBlueprint::GetGlobalRuntimeParams() const
138 return m_globalRuntimeParams;
141 const ITextualGeneratorBlueprint* CTextualQueryBlueprint::GetGenerator() const
143 return m_pGenerator.get();
146 const ITextualEvaluatorBlueprint& CTextualQueryBlueprint::GetInstantEvaluator(size_t index) const
148 assert(index < m_instantEvaluators.size());
149 return *m_instantEvaluators[index];
152 const ITextualEvaluatorBlueprint& CTextualQueryBlueprint::GetDeferredEvaluator(size_t index) const
154 assert(index < m_deferredEvaluators.size());
155 return *m_deferredEvaluators[index];
158 size_t CTextualQueryBlueprint::GetChildCount() const
160 return m_children.size();
163 const ITextualQueryBlueprint& CTextualQueryBlueprint::GetChild(size_t index) const
165 assert(index < m_children.size());
166 return *m_children[index];
169 void CTextualQueryBlueprint::SetSyntaxErrorCollector(DataSource::SyntaxErrorCollectorUniquePtr pSyntaxErrorCollector)
171 m_pSyntaxErrorCollector = std::move(pSyntaxErrorCollector);
174 DataSource::ISyntaxErrorCollector* CTextualQueryBlueprint::GetSyntaxErrorCollector() const
176 return m_pSyntaxErrorCollector.get();
179 //===================================================================================
181 // CQueryBlueprint
183 //===================================================================================
185 CQueryBlueprint::CQueryBlueprint()
186 : m_pQueryFactory(nullptr)
187 , m_maxItemsToKeepInResultSet(0)
188 , m_pParent(nullptr)
190 // nothing
193 CQueryBlueprint::~CQueryBlueprint()
195 for (CInstantEvaluatorBlueprint* pIE : m_instantEvaluators)
197 delete pIE;
200 for (CDeferredEvaluatorBlueprint* pDE : m_deferredEvaluators)
202 delete pDE;
206 const char* CQueryBlueprint::GetName() const
208 return m_name.c_str();
211 void CQueryBlueprint::VisitRuntimeParams(Client::IQueryBlueprintRuntimeParamVisitor& visitor) const
213 std::map<string, Client::IItemFactory*> allRuntimeParamsInTheHierarchy;
215 GrabRuntimeParamsRecursively(allRuntimeParamsInTheHierarchy);
217 for (const auto& pair : allRuntimeParamsInTheHierarchy)
219 const char* szParamName = pair.first.c_str();
220 Client::IItemFactory* pItemFactory = pair.second;
221 assert(pItemFactory);
222 visitor.OnRuntimeParamVisited(szParamName, *pItemFactory);
226 const Shared::CTypeInfo& CQueryBlueprint::GetOutputType() const
228 assert(m_pQueryFactory);
229 return m_pQueryFactory->GetQueryBlueprintType(*this);
232 bool CQueryBlueprint::Resolve(const ITextualQueryBlueprint& source)
234 // name
235 m_name = source.GetName();
237 // query factory: first search by its GUID, then by its name
239 const CryGUID& queryFactoryGUID = source.GetQueryFactoryGUID();
240 const char* szQueryFactoryName = source.GetQueryFactoryName();
242 if(!(m_pQueryFactory = static_cast<CQueryFactoryBase*>(g_pHub->GetQueryFactoryDatabase().FindFactoryByGUID(queryFactoryGUID)))) // the static_cast<> is kinda ok'ish here, since IQueryFactory and its derived class CQueryFactoryBase are _both_ defined in the core, so we definitely know about the inheritance hierarchy
244 if (!(m_pQueryFactory = static_cast<CQueryFactoryBase*>(g_pHub->GetQueryFactoryDatabase().FindFactoryByName(szQueryFactoryName)))) // ditto
246 if (DataSource::ISyntaxErrorCollector* pSE = source.GetSyntaxErrorCollector())
248 Shared::CUqsString guidAsString;
249 Shared::Internal::CGUIDHelper::ToString(queryFactoryGUID, guidAsString);
250 pSE->AddErrorMessage("Unknown QueryFactory: GUID = %s, name = '%s'", guidAsString.c_str(), szQueryFactoryName);
252 return false;
257 // max. items to keep in result set
258 m_maxItemsToKeepInResultSet = source.GetMaxItemsToKeepInResultSet();
260 // ensure no duplicates between constant-params and runtime-params
261 for (size_t i1 = 0; i1 < source.GetGlobalConstantParams().GetParameterCount(); ++i1)
263 for (size_t i2 = 0; i2 < source.GetGlobalRuntimeParams().GetParameterCount(); ++i2)
265 const CTextualGlobalConstantParamsBlueprint::SParameterInfo p1 = source.GetGlobalConstantParams().GetParameter(i1);
266 const CTextualGlobalRuntimeParamsBlueprint::SParameterInfo p2 = source.GetGlobalRuntimeParams().GetParameter(i2);
268 if (strcmp(p1.szName, p2.szName) == 0)
270 // output error to constant-params
271 if (DataSource::ISyntaxErrorCollector* pSE = p1.pSyntaxErrorCollector)
273 pSE->AddErrorMessage("Global constant-parameter clashes with runtime-parameter of the same name: '%s'", p1.szName);
276 // output error to runtime-params
277 if (DataSource::ISyntaxErrorCollector* pSE = p2.pSyntaxErrorCollector)
279 pSE->AddErrorMessage("Global runtime-parameter clashes with constant-parameter of the same name: '%s'", p2.szName);
282 return false;
287 // global constant-params
288 if (!m_globalConstantParams.Resolve(source.GetGlobalConstantParams()))
290 return false;
293 // global runtime-params
294 if (!m_globalRuntimeParams.Resolve(source.GetGlobalRuntimeParams(), m_pParent))
296 return false;
299 // generator (a generator is optional and typically never exists in composite queries)
300 if (const ITextualGeneratorBlueprint* pGeneratorBlueprint = source.GetGenerator())
302 m_pGenerator.reset(new CGeneratorBlueprint);
303 if (!m_pGenerator->Resolve(*pGeneratorBlueprint, *this))
305 m_pGenerator.reset(); // nullify the generator so that CInputBlueprint::Resolve() won't be tempted to use this half-baked object for further checks (and crash becuase it might be lacking a generator-factory)
306 return false;
310 // instant-evaluators
311 for (size_t i = 0; i < source.GetInstantEvaluatorCount(); ++i)
313 CInstantEvaluatorBlueprint* pNewInstantEvaluatorBP = new CInstantEvaluatorBlueprint;
314 m_instantEvaluators.push_back(pNewInstantEvaluatorBP);
315 if (!pNewInstantEvaluatorBP->Resolve(source.GetInstantEvaluator(i), *this))
317 return false;
321 // deferred-evaluators
322 for (size_t i = 0; i < source.GetDeferredEvaluatorCount(); ++i)
324 CDeferredEvaluatorBlueprint* pNewDeferredEvaluatorBP = new CDeferredEvaluatorBlueprint;
325 m_deferredEvaluators.push_back(pNewDeferredEvaluatorBP);
326 if (!pNewDeferredEvaluatorBP->Resolve(source.GetDeferredEvaluator(i), *this))
328 return false;
333 // - ensure that the max. number of instant- and deferred-evaluators is not exceeded
334 // - otherwise, CQuery cannot keep track of all evaluator's states as a bitfield during query execution
338 const size_t numInstantEvaluators = m_instantEvaluators.size();
339 if (numInstantEvaluators > UQS_MAX_EVALUATORS)
341 if (DataSource::ISyntaxErrorCollector* pSE = source.GetSyntaxErrorCollector())
343 pSE->AddErrorMessage("Exceeded the maximum number of instant-evaluators in the query blueprint (max %i supported, %i present in the blueprint)", UQS_MAX_EVALUATORS, (int)numInstantEvaluators);
345 return false;
350 const size_t numDeferredEvaluators = m_deferredEvaluators.size();
351 if (numDeferredEvaluators > UQS_MAX_EVALUATORS)
353 if (DataSource::ISyntaxErrorCollector* pSE = source.GetSyntaxErrorCollector())
355 pSE->AddErrorMessage("Exceeded the maximum number of deferred-evaluators in the query blueprint (max %i supported, %i present in the blueprint)", UQS_MAX_EVALUATORS, (int)numDeferredEvaluators);
357 return false;
361 // children (recurse down)
362 for (size_t i = 0, n = source.GetChildCount(); i < n; ++i)
364 const ITextualQueryBlueprint& childSource = source.GetChild(i);
365 std::shared_ptr<CQueryBlueprint> pChildTarget(new CQueryBlueprint);
366 pChildTarget->m_pParent = this;
367 m_children.push_back(pChildTarget);
368 if (!pChildTarget->Resolve(childSource))
369 return false;
372 // if the query-factory expects to have a generator in the blueprint, then ensure that one was provided (and the other way around)
373 if (m_pQueryFactory) // might be a nullptr in case of unknown query factory (c. f. code further above)
375 if (m_pQueryFactory->RequiresGenerator() && !m_pGenerator)
377 if (DataSource::ISyntaxErrorCollector* pSE = source.GetSyntaxErrorCollector())
379 pSE->AddErrorMessage("Query factories of type '%s' require a generator, but none is provided", m_pQueryFactory->GetName());
381 return false;
383 else if (!m_pQueryFactory->RequiresGenerator() && m_pGenerator)
385 if (DataSource::ISyntaxErrorCollector* pSE = source.GetSyntaxErrorCollector())
387 pSE->AddErrorMessage("Query factories of type '%s' don't require a generator, but one was provided", m_pQueryFactory->GetName());
389 return false;
393 // validate the min/max number of child queries
394 if (m_pQueryFactory) // might be a nullptr in case of unknown query factory (c. f. code further above)
396 const size_t actualNumChildQueries = m_children.size();
398 // min required child queries
400 const size_t minRequiredChildQueries = m_pQueryFactory->GetMinRequiredChildren();
401 if (actualNumChildQueries < minRequiredChildQueries)
403 if (DataSource::ISyntaxErrorCollector* pSE = source.GetSyntaxErrorCollector())
405 pSE->AddErrorMessage("Too few child queries: %i (expected at least %i)", (int)actualNumChildQueries, (int)minRequiredChildQueries);
407 return false;
411 // max allowed child queries
413 const size_t maxAllowedChildQueries = m_pQueryFactory->GetMaxAllowedChildren();
414 if (actualNumChildQueries > maxAllowedChildQueries)
416 if (DataSource::ISyntaxErrorCollector* pSE = source.GetSyntaxErrorCollector())
418 pSE->AddErrorMessage("Too many child queries: %i (expected no more than %i)", (int)actualNumChildQueries, (int)maxAllowedChildQueries);
420 return false;
425 // have the query-factory check that if it deals with child-queries that their output types are compatible among each other
427 string error;
428 size_t indexOfChildCausingTheError = 0;
430 if (!m_pQueryFactory->CheckOutputTypeCompatibilityAmongChildQueryBlueprints(*this, error, indexOfChildCausingTheError))
432 // write the error message into the child that caused the error
433 const ITextualQueryBlueprint& childSource = source.GetChild(indexOfChildCausingTheError);
434 if (DataSource::ISyntaxErrorCollector* pSE = childSource.GetSyntaxErrorCollector())
436 pSE->AddErrorMessage("%s", error.c_str());
438 return false;
442 // sort the instant-evaluator blueprints by cost and evaluation modality such that their order at execution time won't change
443 // (this also helps the user to read the query history as it will show all evaluators in order of how they were executed)
444 SortInstantEvaluatorBlueprintsByCostAndEvaluationModality();
446 return true;
449 const CQueryBlueprint* CQueryBlueprint::GetParent() const
451 return m_pParent;
454 size_t CQueryBlueprint::GetChildCount() const
456 return m_children.size();
459 std::shared_ptr<const CQueryBlueprint> CQueryBlueprint::GetChild(size_t index) const
461 assert(index < m_children.size());
462 return m_children[index];
465 int CQueryBlueprint::GetChildIndex(const CQueryBlueprint* pChildToSearchFor) const
467 for (size_t i = 0; i < m_children.size(); ++i)
469 if (m_children[i].get() == pChildToSearchFor)
470 return (int)i;
472 return -1;
475 QueryBaseUniquePtr CQueryBlueprint::CreateQuery(const CQueryBase::SCtorContext& ctorContext) const
477 assert(m_pQueryFactory);
478 return m_pQueryFactory->CreateQuery(ctorContext);
481 int CQueryBlueprint::GetMaxItemsToKeepInResultSet() const
483 return m_maxItemsToKeepInResultSet;
486 const CGlobalConstantParamsBlueprint& CQueryBlueprint::GetGlobalConstantParamsBlueprint() const
488 return m_globalConstantParams;
491 const CGlobalRuntimeParamsBlueprint& CQueryBlueprint::GetGlobalRuntimeParamsBlueprint() const
493 return m_globalRuntimeParams;
496 const CGeneratorBlueprint* CQueryBlueprint::GetGeneratorBlueprint() const
498 return m_pGenerator.get();
501 const std::vector<CInstantEvaluatorBlueprint*>& CQueryBlueprint::GetInstantEvaluatorBlueprints() const
503 return m_instantEvaluators;
506 const std::vector<CDeferredEvaluatorBlueprint*>& CQueryBlueprint::GetDeferredEvaluatorBlueprints() const
508 return m_deferredEvaluators;
511 bool CQueryBlueprint::CheckPresenceAndTypeOfGlobalRuntimeParamsRecursively(const Shared::IVariantDict& runtimeParamsToValidate, Shared::CUqsString& error) const
514 // ensure that all required runtime-params have been passed in and that their data types match those in this query-blueprint
517 const std::map<string, CGlobalRuntimeParamsBlueprint::SParamInfo>& expectedRuntimeParams = m_globalRuntimeParams.GetParams();
519 for (const auto& pair : expectedRuntimeParams)
521 const char* szKey = pair.first.c_str();
522 const Client::IItemFactory* pFoundItemFactory = runtimeParamsToValidate.FindItemFactory(szKey);
524 if (pFoundItemFactory == nullptr)
526 error.Format("Missing runtime-param: '%s'", szKey);
527 return false;
530 const Client::IItemFactory* pExpectedItemFactory = pair.second.pItemFactory;
531 assert(pExpectedItemFactory);
533 if (pFoundItemFactory != pExpectedItemFactory)
535 error.Format("Runtime-param '%s' mismatches the item-factory: expected '%s', but received '%s'", szKey, pExpectedItemFactory->GetName(), pFoundItemFactory->GetName());
536 return false;
540 // recurse down
541 for (const std::shared_ptr<CQueryBlueprint>& pChild : m_children)
543 if (!pChild->CheckPresenceAndTypeOfGlobalRuntimeParamsRecursively(runtimeParamsToValidate, error))
545 return false;
549 return true;
552 const Shared::CTypeInfo* CQueryBlueprint::GetTypeOfShuttledItemsToExpect() const
554 assert(m_pQueryFactory);
555 return m_pQueryFactory->GetTypeOfShuttledItemsToExpect(*this);
558 const CQueryFactoryBase& CQueryBlueprint::GetQueryFactory() const
560 assert(m_pQueryFactory);
561 return *m_pQueryFactory;
564 void CQueryBlueprint::PrintToConsole(CLogger& logger) const
567 // name + output type + max items to generate
570 const Shared::CTypeInfo& outputType = GetOutputType();
571 logger.Printf("\"%s\" [%s] (max items in result set = %i)", m_name.c_str(), outputType.name(), m_maxItemsToKeepInResultSet);
572 CLoggerIndentation _indent;
575 // factory
578 assert(m_pQueryFactory);
579 logger.Printf("Query factory = %s", m_pQueryFactory->GetName());
582 // global constant params
585 m_globalConstantParams.PrintToConsole(logger);
588 // global runtime params
591 m_globalRuntimeParams.PrintToConsole(logger);
594 // generator (optional - typically never used by composite queries)
597 if (m_pGenerator)
599 m_pGenerator->PrintToConsole(logger);
601 else
603 logger.Printf("Generator: (none)");
607 // instant-evaluators
610 if (m_instantEvaluators.empty())
612 logger.Printf("Instant Evaluators: (none)");
614 else
616 logger.Printf("Instant Evaluators:");
617 CLoggerIndentation _indent;
618 for (size_t i = 0, n = m_instantEvaluators.size(); i < n; ++i)
620 stack_string prefix;
621 prefix.Format("#%i: ", (int)i);
622 m_instantEvaluators[i]->PrintToConsole(logger, prefix.c_str());
627 // deferred-evaluators
630 if (m_deferredEvaluators.empty())
632 logger.Printf("Deferred Evaluators: (none)");
634 else
636 logger.Printf("Deferred Evaluators:");
637 CLoggerIndentation _indent;
638 for (size_t i = 0, n = m_deferredEvaluators.size(); i < n; ++i)
640 stack_string prefix;
641 prefix.Format("#%i: ", (int)i);
642 m_deferredEvaluators[i]->PrintToConsole(logger, prefix.c_str());
647 // children
650 if (m_children.empty())
652 logger.Printf("Children: (none)");
654 else
656 logger.Printf("Children: %i", (int)m_children.size());
657 for (size_t i = 0, n = m_children.size(); i < n; ++i)
659 logger.Printf("Child %i:", (int)i);
660 CLoggerIndentation _indent;
661 m_children[i]->PrintToConsole(logger);
666 void CQueryBlueprint::SortInstantEvaluatorBlueprintsByCostAndEvaluationModality()
669 // sort all instant-evaluator blueprints such that we end up with the following order:
671 // cheap tester1
672 // cheap tester2
673 // cheap tester3
674 // ...
675 // cheap scorer1
676 // cheap scorer2
677 // cheap scorer3
678 // ...
679 // expensive tester1
680 // expensive tester2
681 // expensive tester3
682 // ...
683 // expensive scorer1
684 // expensive scorer2
685 // expensive scorer3
686 // ...
689 std::vector<CInstantEvaluatorBlueprint *> cheapTesters;
690 std::vector<CInstantEvaluatorBlueprint *> cheapScorers;
691 std::vector<CInstantEvaluatorBlueprint *> expensiveTesters;
692 std::vector<CInstantEvaluatorBlueprint *> expensiveScorers;
694 for (CInstantEvaluatorBlueprint* pIEBlueprint : m_instantEvaluators)
696 const Client::IInstantEvaluatorFactory& factory = pIEBlueprint->GetFactory();
697 const Client::IInstantEvaluatorFactory::ECostCategory costCategory = factory.GetCostCategory();
698 const Client::IInstantEvaluatorFactory::EEvaluationModality evaluationModality = factory.GetEvaluationModality();
700 switch (costCategory)
702 case Client::IInstantEvaluatorFactory::ECostCategory::Cheap:
703 switch (evaluationModality)
705 case Client::IInstantEvaluatorFactory::EEvaluationModality::Testing:
706 cheapTesters.push_back(pIEBlueprint);
707 break;
709 case Client::IInstantEvaluatorFactory::EEvaluationModality::Scoring:
710 cheapScorers.push_back(pIEBlueprint);
711 break;
713 default:
714 assert(0);
716 break;
718 case Client::IInstantEvaluatorFactory::ECostCategory::Expensive:
719 switch (evaluationModality)
721 case Client::IInstantEvaluatorFactory::EEvaluationModality::Testing:
722 expensiveTesters.push_back(pIEBlueprint);
723 break;
725 case Client::IInstantEvaluatorFactory::EEvaluationModality::Scoring:
726 expensiveScorers.push_back(pIEBlueprint);
727 break;
729 default:
730 assert(0);
732 break;
734 default:
735 assert(0);
739 m_instantEvaluators.clear();
741 m_instantEvaluators.insert(m_instantEvaluators.end(), cheapTesters.begin(), cheapTesters.end());
742 m_instantEvaluators.insert(m_instantEvaluators.end(), cheapScorers.begin(), cheapScorers.end());
743 m_instantEvaluators.insert(m_instantEvaluators.end(), expensiveTesters.begin(), expensiveTesters.end());
744 m_instantEvaluators.insert(m_instantEvaluators.end(), expensiveScorers.begin(), expensiveScorers.end());
747 void CQueryBlueprint::GrabRuntimeParamsRecursively(std::map<string, Client::IItemFactory*>& out) const
749 const std::map<string, CGlobalRuntimeParamsBlueprint::SParamInfo>& params = m_globalRuntimeParams.GetParams();
751 for (const auto& pair : params)
753 const char* szParamName = pair.first.c_str();
754 Client::IItemFactory* pItemFactory = pair.second.pItemFactory;
755 assert(pItemFactory);
756 out[szParamName] = pItemFactory; // potentially overwrite the param from a parent; we presume here that both have the same item type (this is ensured by CGlobalRuntimeParamsBlueprint::Resolve())
759 // recurse down all children
760 for (const std::shared_ptr<CQueryBlueprint>& pChild : m_children)
762 pChild->GrabRuntimeParamsRecursively(out);