1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Merge pull request #1177 from IvanSavenko/fix_mod_identifiers

Fix mod identifiers registration & incorrect mod validation
This commit is contained in:
Andrii Danylchenko 2022-12-02 09:20:49 +02:00 committed by GitHub
commit d9c4b28ccc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 64 additions and 38 deletions

View File

@ -373,8 +373,8 @@ CArtifact * CArtHandler::loadFromJson(const std::string & scope, const JsonNode
if(!art->advMapDef.empty())
{
JsonNode templ;
templ.setMeta(scope);
templ["animation"].String() = art->advMapDef;
templ.setMeta(scope);
// add new template.
// Necessary for objects added via mods that don't have any templates in H3

View File

@ -633,6 +633,7 @@ CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const Json
{
JsonNode templ;
templ["animation"].String() = cre->advMapDef;
templ.setMeta(scope);
VLC->objtypeh->getHandlerFor(Obj::MONSTER, cre->idNumber.num)->addTemplate(templ);
}

View File

@ -206,39 +206,54 @@ std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdent
std::set<std::string> allowedScopes;
bool isValidScope = true;
// called have not specified destination mod explicitly
if (request.remoteScope.empty())
{
// FIXME: temporary, for queries from map loader allow access to any identifer
// should be changed to list of mods that are marked as required by current map
if (request.localScope == "map")
{
for (auto const & modName : VLC->modh->getActiveMods())
allowedScopes.insert(modName);
}
// normally ID's from all required mods, own mod and virtual "core" mod are allowed
if(request.localScope != "core" && !request.localScope.empty())
else if(request.localScope != "core" && !request.localScope.empty())
{
allowedScopes = VLC->modh->getModDependencies(request.localScope, isValidScope);
if(!isValidScope)
return std::vector<ObjectData>();
allowedScopes.insert(request.localScope);
}
allowedScopes.insert(request.localScope);
// all mods can access built-in mod
allowedScopes.insert("core");
}
else
{
//...unless destination mod was specified explicitly
//note: getModDependencies does not work for "core" by design
//if destination mod was specified explicitly, restrict lookup to this mod
//for map format support core mod has access to any mod
//TODO: better solution for access from map?
if(request.localScope == "core" || request.localScope.empty())
if(request.remoteScope == "core" )
{
//"core" mod is an implicit dependency for all mods, allow access into it
allowedScopes.insert(request.remoteScope);
}
else if(request.remoteScope == request.localScope )
{
// allow self-access
allowedScopes.insert(request.remoteScope);
}
else
{
// allow only available to all core mod or dependencies
// allow access only if mod is in our dependencies
auto myDeps = VLC->modh->getModDependencies(request.localScope, isValidScope);
if(!isValidScope)
return std::vector<ObjectData>();
if(request.remoteScope == "core" || request.remoteScope == request.localScope || myDeps.count(request.remoteScope))
if(myDeps.count(request.remoteScope))
allowedScopes.insert(request.remoteScope);
}
}
@ -296,10 +311,14 @@ void CIdentifierStorage::finalize()
state = FINALIZING;
bool errorsFound = false;
//Note: we may receive new requests during resolution phase -> end may change -> range for can't be used
for(auto it = scheduledRequests.begin(); it != scheduledRequests.end(); it++)
while ( !scheduledRequests.empty() )
{
errorsFound |= !resolveIdentifier(*it);
// Use local copy since new requests may appear during resolving, invalidating any iterators
auto request = scheduledRequests.back();
scheduledRequests.pop_back();
if (!resolveIdentifier(request))
errorsFound = true;
}
if (errorsFound)

View File

@ -1014,7 +1014,7 @@ const JsonNode & JsonUtils::getSchema(std::string URI)
return getSchemaByName(filename).resolvePointer(URI.substr(posHash + 1));
}
void JsonUtils::merge(JsonNode & dest, JsonNode & source, bool noOverride)
void JsonUtils::merge(JsonNode & dest, JsonNode & source, bool ignoreOverride, bool copyMeta)
{
if (dest.getType() == JsonNode::JsonType::DATA_NULL)
{
@ -1022,6 +1022,14 @@ void JsonUtils::merge(JsonNode & dest, JsonNode & source, bool noOverride)
return;
}
bool hasNull = dest.isNull() || source.isNull();
bool sameType = dest.getType() == source.getType();
bool sourceNumeric = source.getType() == JsonNode::JsonType::DATA_FLOAT || source.getType() == JsonNode::JsonType::DATA_INTEGER;
bool destNumeric = dest.getType() == JsonNode::JsonType::DATA_FLOAT || dest.getType() == JsonNode::JsonType::DATA_INTEGER;
bool bothNumeric = sourceNumeric && destNumeric;
assert( hasNull || sameType || bothNumeric );
switch (source.getType())
{
case JsonNode::JsonType::DATA_NULL:
@ -1040,30 +1048,33 @@ void JsonUtils::merge(JsonNode & dest, JsonNode & source, bool noOverride)
}
case JsonNode::JsonType::DATA_STRUCT:
{
if(!noOverride && vstd::contains(source.flags, "override"))
if(!ignoreOverride && vstd::contains(source.flags, "override"))
{
std::swap(dest, source);
}
else
{
if (copyMeta)
dest.meta = source.meta;
//recursively merge all entries from struct
for(auto & node : source.Struct())
merge(dest[node.first], node.second, noOverride);
merge(dest[node.first], node.second, ignoreOverride);
}
}
}
}
void JsonUtils::mergeCopy(JsonNode & dest, JsonNode source, bool noOverride)
void JsonUtils::mergeCopy(JsonNode & dest, JsonNode source, bool ignoreOverride, bool copyMeta)
{
// uses copy created in stack to safely merge two nodes
merge(dest, source, noOverride);
merge(dest, source, ignoreOverride, copyMeta);
}
void JsonUtils::inherit(JsonNode & descendant, const JsonNode & base)
{
JsonNode inheritedNode(base);
merge(inheritedNode, descendant, true);
merge(inheritedNode, descendant, true, true);
descendant.swap(inheritedNode);
}

View File

@ -184,7 +184,7 @@ namespace JsonUtils
* null : if value in source is present but set to null it will delete entry in dest
* @note this function will destroy data in source
*/
DLL_LINKAGE void merge(JsonNode & dest, JsonNode & source, bool noOverride = false);
DLL_LINKAGE void merge(JsonNode & dest, JsonNode & source, bool ignoreOverride = false, bool copyMeta = false);
/**
* @brief recursively merges source into dest, replacing identical fields
@ -194,7 +194,7 @@ namespace JsonUtils
* null : if value in source is present but set to null it will delete entry in dest
* @note this function will preserve data stored in source by creating copy
*/
DLL_LINKAGE void mergeCopy(JsonNode & dest, JsonNode source, bool noOverride = false);
DLL_LINKAGE void mergeCopy(JsonNode & dest, JsonNode source, bool ignoreOverride = false, bool copyMeta = false);
/** @brief recursively merges descendant into copy of base node
* Result emulates inheritance semantic

View File

@ -26,14 +26,6 @@
VCMI_LIB_NAMESPACE_BEGIN
// FIXME: move into inheritNode?
static void inheritNodeWithMeta(JsonNode & descendant, const JsonNode & base)
{
std::string oldMeta = descendant.meta;
JsonUtils::inherit(descendant, base);
descendant.setMeta(oldMeta);
}
CObjectClassesHandler::CObjectClassesHandler()
{
#define SET_HANDLER_CLASS(STRING, CLASSNAME) handlerConstructors[STRING] = std::make_shared<CLASSNAME>;
@ -291,8 +283,9 @@ void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNo
assert(objects.at(ID)->subObjects.count(subID.get()) == 0);
assert(config["index"].isNull());
config["index"].Float() = subID.get();
config["index"].setMeta(config.meta);
}
inheritNodeWithMeta(config, objects.at(ID)->base);
JsonUtils::inherit(config, objects.at(ID)->base);
loadObjectEntry(identifier, config, objects[ID], isSubObject);
}
@ -319,9 +312,9 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype)
throw std::runtime_error("Object type handler not found");
}
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(std::string type, std::string subtype) const
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(std::string scope, std::string type, std::string subtype) const
{
boost::optional<si32> id = VLC->modh->identifiers.getIdentifier("core", "object", type, false);
boost::optional<si32> id = VLC->modh->identifiers.getIdentifier(scope, "object", type, false);
if(id)
{
auto object = objects.at(id.get());
@ -367,11 +360,9 @@ void CObjectClassesHandler::beforeValidate(JsonNode & object)
{
for (auto & entry : object["types"].Struct())
{
inheritNodeWithMeta(entry.second, object["base"]);
JsonUtils::inherit(entry.second, object["base"]);
for (auto & templ : entry.second["templates"].Struct())
{
inheritNodeWithMeta(templ.second, entry.second["base"]);
}
JsonUtils::inherit(templ.second, entry.second["base"]);
}
}

View File

@ -303,7 +303,7 @@ public:
/// returns handler for specified object (ID-based). ObjectHandler keeps ownership
TObjectTypeHandler getHandlerFor(si32 type, si32 subtype) const;
TObjectTypeHandler getHandlerFor(std::string type, std::string subtype) const;
TObjectTypeHandler getHandlerFor(std::string scope, std::string type, std::string subtype) const;
TObjectTypeHandler getHandlerFor(CompoundMapObjectID compoundIdentifier) const;
std::string getObjectName(si32 type) const;

View File

@ -44,6 +44,10 @@ void CTownInstanceConstructor::initTypeData(const JsonNode & input)
});
filtersJson = input["filters"];
// change scope of "filters" to scope of object that is being loaded
// since this filters require to resolve building ID's
filtersJson.setMeta(input["faction"].meta);
}
void CTownInstanceConstructor::afterLoadFinalization()

View File

@ -1121,7 +1121,7 @@ void CMapLoaderJson::MapObjectLoader::construct()
return;
}
auto handler = VLC->objtypeh->getHandlerFor(typeName, subtypeName);
auto handler = VLC->objtypeh->getHandlerFor( "map", typeName, subtypeName);
auto appearance = new ObjectTemplate;