Index: binaries/data/mods/public/gui/credits/texts/programming.json =================================================================== --- binaries/data/mods/public/gui/credits/texts/programming.json +++ binaries/data/mods/public/gui/credits/texts/programming.json @@ -236,6 +236,7 @@ { "nick": "temple" }, { "nick": "texane" }, { "nick": "thamlett", "name": "Timothy Hamlett" }, + { "nick": "TheCake91", "name": "Samuel Egger" }, { "nick": "thedrunkyak", "name": "Dan Fuhr" }, { "nick": "TrinityDeath", "name": "Jethro Lu" }, { "nick": "triumvir", "name": "Corin Schedler" }, Index: binaries/data/mods/public/simulation/templates/special/filter/corpse.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special/filter/corpse.xml +++ binaries/data/mods/public/simulation/templates/special/filter/corpse.xml @@ -1,31 +1,4 @@ - - - - - - - true - - - - - - false - - + - - - - - true - - - - false - Index: binaries/data/mods/public/simulation/templates/template_remains.xml =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/templates/template_remains.xml @@ -0,0 +1,31 @@ + + + + + + + + true + + + + + + false + + + + + + + + true + + + + false + + Index: source/ps/TemplateLoader.h =================================================================== --- source/ps/TemplateLoader.h +++ source/ps/TemplateLoader.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -74,6 +74,12 @@ bool LoadTemplateFile(const std::string& templateName, int depth); /** + * Loads the given filter template with its parents and applies it on the base template + * in m_TemplateFieldData[templateName]. Returns false on error. + */ + bool ApplyTemplateFilter(const std::string& templateName, const VfsPath& templateFilter, int depth); + + /** * Constructs a standard static-decorative-object template for the given actor */ void ConstructTemplateActor(const std::string& actorName, CParamNode& out); Index: source/ps/TemplateLoader.cpp =================================================================== --- source/ps/TemplateLoader.cpp +++ source/ps/TemplateLoader.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -26,6 +26,7 @@ static const wchar_t TEMPLATE_ROOT[] = L"simulation/templates/"; static const wchar_t ACTOR_ROOT[] = L"art/actors/"; +static const int MAX_INHERITANCE_DEPTH = 100; static CParamNode NULL_NODE(false); @@ -35,8 +36,8 @@ if (m_TemplateFileData.find(templateName) != m_TemplateFileData.end()) return true; - // Handle infinite loops more gracefully than running out of stack space and crashing - if (depth > 100) + // Handle infinite loops more gracefully than running out of stack space and crashing. + if (depth > MAX_INHERITANCE_DEPTH) { LOGERROR("Probable infinite inheritance loop in entity template '%s'", templateName.c_str()); return false; @@ -56,12 +57,15 @@ std::string prefix = templateName.substr(0, pos); std::string baseName = templateName.substr(pos+1); + // Load base template if (!LoadTemplateFile(baseName, depth+1)) { LOGERROR("Failed to load entity template '%s'", baseName.c_str()); return false; } + m_TemplateFileData[templateName] = m_TemplateFileData[baseName]; + VfsPath path = VfsPath(TEMPLATE_ROOT) / L"special" / L"filter" / wstring_from_utf8(prefix + ".xml"); if (!VfsFileExists(path)) { @@ -69,13 +73,13 @@ return false; } - CXeromyces xero; - PSRETURN ok = xero.Load(g_VFS, path); - if (ok != PSRETURN_OK) - return false; // (Xeromyces already logged an error with the full filename) + // Apply filter on the base template. + if (!ApplyTemplateFilter(templateName, path, 0)) + { + LOGERROR("Failed to load template filter '%s'", prefix.c_str()); + return false; + } - m_TemplateFileData[templateName] = m_TemplateFileData[baseName]; - CParamNode::LoadXML(m_TemplateFileData[templateName], xero, path.string().c_str()); return true; } @@ -117,6 +121,44 @@ return true; } +bool CTemplateLoader::ApplyTemplateFilter(const std::string& templateName, const VfsPath& templateFilter, int depth) +{ + // Handle infinite loops more gracefully than running out of stack space and crashing. + if (depth > MAX_INHERITANCE_DEPTH) + { + LOGERROR("Probable infinite inheritance loop in template filter '%s'", templateName.c_str()); + return false; + } + + VfsPath path = templateFilter; + CXeromyces xero; + if (xero.Load(g_VFS, path) != PSRETURN_OK) + return false; // (Xeromyces already logged an error with the full filename) + + int attr_parent = xero.GetAttributeID("parent"); + CStr parentName = xero.GetRoot().GetAttributes().GetNamedItem(attr_parent); + if (!parentName.empty()) + { + VfsPath path = VfsPath(TEMPLATE_ROOT) / wstring_from_utf8(parentName + ".xml"); + if (!VfsFileExists(path)) + { + LOGERROR("Invalid subset '%s'", parentName.c_str()); + return false; + } + + // Ensure the parent is loaded + if (!ApplyTemplateFilter(templateName, path, depth + 1)) + { + LOGERROR("Failed to load parent '%s' of template filter '%s'", parentName.c_str(), templateName.c_str()); + return false; + } + } + + CParamNode::LoadXML(m_TemplateFileData[templateName], xero, wstring_from_utf8(templateName).c_str()); + + return true; +} + static Status AddToTemplates(const VfsPath& pathname, const CFileInfo& UNUSED(fileInfo), const uintptr_t cbData) { std::vector& templates = *(std::vector*)cbData;