Index: source/graphics/MapGenerator.cpp =================================================================== --- source/graphics/MapGenerator.cpp +++ source/graphics/MapGenerator.cpp @@ -248,7 +248,7 @@ CParamNode CMapGeneratorWorker::GetTemplate(const std::string& templateName) { - const CParamNode& templateRoot = m_TemplateLoader.GetTemplateFileData(templateName).GetChild("Entity"); + const CParamNode& templateRoot = m_TemplateLoader.GetTemplateFileData(templateName).GetOnlyChild(); if (!templateRoot.IsOk()) LOGERROR("Invalid template found for '%s'", templateName.c_str()); Index: source/gui/GUIManager.cpp =================================================================== --- source/gui/GUIManager.cpp +++ source/gui/GUIManager.cpp @@ -415,7 +415,7 @@ const CParamNode& CGUIManager::GetTemplate(const std::string& templateName) { - const CParamNode& templateRoot = m_TemplateLoader.GetTemplateFileData(templateName).GetChild("Entity"); + const CParamNode& templateRoot = m_TemplateLoader.GetTemplateFileData(templateName).GetOnlyChild(); if (!templateRoot.IsOk()) LOGERROR("Invalid template found for '%s'", templateName.c_str()); Index: source/ps/GameSetup/GameSetup.cpp =================================================================== --- source/ps/GameSetup/GameSetup.cpp +++ source/ps/GameSetup/GameSetup.cpp @@ -787,7 +787,7 @@ // This is very cheap to create so let's just do it every time. CTemplateLoader templateLoader; - const CParamNode& templateRoot = templateLoader.GetTemplateFileData(templateName).GetChild("Entity"); + const CParamNode& templateRoot = templateLoader.GetTemplateFileData(templateName).GetOnlyChild(); if (!templateRoot.IsOk()) LOGERROR("Invalid template found for '%s'", templateName.c_str()); Index: source/simulation2/components/CCmpAIManager.cpp =================================================================== --- source/simulation2/components/CCmpAIManager.cpp +++ source/simulation2/components/CCmpAIManager.cpp @@ -354,7 +354,7 @@ { if (!m_TemplateLoader.TemplateExists(name)) return CParamNode(false); - return m_TemplateLoader.GetTemplateFileData(name).GetChild("Entity"); + return m_TemplateLoader.GetTemplateFileData(name).GetOnlyChild(); } /** Index: source/simulation2/components/CCmpTemplateManager.cpp =================================================================== --- source/simulation2/components/CCmpTemplateManager.cpp +++ source/simulation2/components/CCmpTemplateManager.cpp @@ -170,7 +170,7 @@ return NULL; } - const CParamNode& templateRoot = fileData.GetChild("Entity"); + const CParamNode& templateRoot = fileData.GetOnlyChild(); if (!templateRoot.IsOk()) { // The validator should never let this happen @@ -183,7 +183,7 @@ const CParamNode* CCmpTemplateManager::GetTemplateWithoutValidation(const std::string& templateName) { - const CParamNode& templateRoot = m_templateLoader.GetTemplateFileData(templateName).GetChild("Entity"); + const CParamNode& templateRoot = m_templateLoader.GetTemplateFileData(templateName).GetOnlyChild(); if (!templateRoot.IsOk()) return NULL; Index: source/simulation2/system/ComponentManager.cpp =================================================================== --- source/simulation2/system/ComponentManager.cpp +++ source/simulation2/system/ComponentManager.cpp @@ -1145,7 +1145,8 @@ std::sort(componentTypes.begin(), componentTypes.end()); schema += "" - "" + "" + "" ""; for (std::vector::const_iterator it = componentTypes.begin(); it != componentTypes.end(); ++it) schema += ""; Index: source/simulation2/system/ParamNode.h =================================================================== --- source/simulation2/system/ParamNode.h +++ source/simulation2/system/ParamNode.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -191,6 +191,12 @@ // a node is always modified explicitly and not indirectly via its children, e.g. to cache JS::Values) /** + * Returns the only child node, or a node with IsOk() == false if there is none. + * This is mainly useful for the root node. + */ + const CParamNode& GetOnlyChild() const; + + /** * Returns true if this is a valid CParamNode, false if it represents a non-existent node */ bool IsOk() const; Index: source/simulation2/system/ParamNode.cpp =================================================================== --- source/simulation2/system/ParamNode.cpp +++ source/simulation2/system/ParamNode.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -230,6 +230,15 @@ } } +const CParamNode& CParamNode::GetOnlyChild() const +{ + if (m_Childs.empty()) + return g_NullNode; + + ENSURE(m_Childs.size() == 1); + return m_Childs.begin()->second; +} + const CParamNode& CParamNode::GetChild(const char* name) const { ChildrenMap::const_iterator it = m_Childs.find(name); Index: source/simulation2/tests/test_ParamNode.h =================================================================== --- source/simulation2/tests/test_ParamNode.h +++ source/simulation2/tests/test_ParamNode.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -50,6 +50,7 @@ TS_ASSERT_EQUALS(node.GetChild("test").GetChild("Bar").GetChild("Baz").ToInt(), 3); TS_ASSERT(node.GetChild("test").GetChild("Qux").IsOk()); TS_ASSERT(!node.GetChild("test").GetChild("Qux").GetChild("Baz").IsOk()); + TS_ASSERT_STR_EQUALS(node.GetChild("test").ToXMLString(), node.GetOnlyChild().ToXMLString()); CParamNode nullOne(false); CParamNode nullTwo = nullOne;