Pārlūkot izejas kodu

Split massive CModHandler class/file into multiple parts:

- IdentifierStorage is now a separate handler in VLC
- Renamed ModHandler::Incompatibility exception to ModIncompatibility
- Extracted ModScope namespace from ModHandler
- Extracted ModUtilities namespace from ModHandler
- Split CModHandler.cpp on per-class basis
- Replaced some direct members with unique_ptr to reduce header includes
Ivan Savenko 2 gadi atpakaļ
vecāks
revīzija
62fddca21e
100 mainītis faili ar 1528 papildinājumiem un 1275 dzēšanām
  1. 6 3
      client/ClientCommandManager.cpp
  2. 0 1
      client/adventureMap/CList.cpp
  3. 2 2
      client/lobby/CLobbyScreen.cpp
  4. 0 1
      client/lobby/RandomMapTab.cpp
  5. 0 1
      client/lobby/SelectionTab.cpp
  6. 3 2
      client/render/Graphics.cpp
  7. 1 1
      client/renderSDL/CBitmapFont.cpp
  8. 0 1
      client/widgets/MiscWidgets.cpp
  9. 0 1
      client/windows/CCastleInterface.cpp
  10. 0 1
      client/windows/CCreatureWindow.cpp
  11. 0 1
      client/windows/CKingdomInterface.cpp
  12. 0 1
      client/windows/GUIClasses.cpp
  13. 10 0
      cmake_modules/VCMI_lib.cmake
  14. 3 1
      launcher/modManager/cmodmanager.cpp
  15. 3 4
      lib/CArtHandler.cpp
  16. 5 5
      lib/CCreatureHandler.cpp
  17. 2 2
      lib/CCreatureSet.cpp
  18. 0 1
      lib/CGameInfoCallback.cpp
  19. 1 1
      lib/CGeneralTextHandler.cpp
  20. 11 11
      lib/CHeroHandler.cpp
  21. 5 3
      lib/CSkillHandler.cpp
  22. 21 20
      lib/CTownHandler.cpp
  23. 8 7
      lib/GameConstants.cpp
  24. 4 1
      lib/IGameCallback.cpp
  25. 5 3
      lib/IHandlerBase.cpp
  26. 4 3
      lib/JsonDetail.cpp
  27. 8 8
      lib/JsonNode.cpp
  28. 11 10
      lib/JsonRandom.cpp
  29. 0 1
      lib/NetPacksLib.cpp
  30. 3 2
      lib/ObstacleHandler.cpp
  31. 1 1
      lib/RiverHandler.cpp
  32. 1 1
      lib/RoadHandler.cpp
  33. 0 1
      lib/ScriptHandler.cpp
  34. 3 3
      lib/StartInfo.cpp
  35. 6 5
      lib/TerrainHandler.cpp
  36. 9 1
      lib/VCMI_Lib.cpp
  37. 2 0
      lib/VCMI_Lib.h
  38. 0 1
      lib/battle/CBattleInfoCallback.cpp
  39. 4 4
      lib/bonuses/Bonus.cpp
  40. 0 1
      lib/bonuses/Limiters.cpp
  41. 9 7
      lib/campaign/CampaignHandler.cpp
  42. 2 2
      lib/filesystem/Filesystem.cpp
  43. 2 1
      lib/gameState/CGameState.cpp
  44. 2 2
      lib/mapObjectConstructors/AObjectTypeHandler.cpp
  45. 6 6
      lib/mapObjectConstructors/CObjectClassesHandler.cpp
  46. 6 5
      lib/mapObjectConstructors/CommonConstructors.cpp
  47. 2 2
      lib/mapObjectConstructors/DwellingInstanceConstructor.cpp
  48. 2 2
      lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp
  49. 2 2
      lib/mapObjects/CGHeroInstance.cpp
  50. 2 2
      lib/mapObjects/CGTownInstance.cpp
  51. 5 4
      lib/mapObjects/CQuest.cpp
  52. 4 4
      lib/mapObjects/MiscObjects.cpp
  53. 2 2
      lib/mapObjects/ObjectTemplate.cpp
  54. 1 1
      lib/mapping/CMapHeader.h
  55. 0 1
      lib/mapping/CMapInfo.cpp
  56. 4 2
      lib/mapping/CMapService.cpp
  57. 0 1
      lib/mapping/MapFormatH3M.cpp
  58. 8 7
      lib/mapping/MapFormatJson.cpp
  59. 5 5
      lib/mapping/MapIdentifiersH3M.cpp
  60. 59 769
      lib/modding/CModHandler.cpp
  61. 22 283
      lib/modding/CModHandler.h
  62. 141 0
      lib/modding/CModInfo.cpp
  63. 80 0
      lib/modding/CModInfo.h
  64. 6 0
      lib/modding/CModVersion.h
  65. 249 0
      lib/modding/ContentTypeHandler.cpp
  66. 77 0
      lib/modding/ContentTypeHandler.h
  67. 339 0
      lib/modding/IdentifierStorage.cpp
  68. 110 0
      lib/modding/IdentifierStorage.h
  69. 42 0
      lib/modding/ModIncompatibility.h
  70. 53 0
      lib/modding/ModScope.h
  71. 77 0
      lib/modding/ModUtility.cpp
  72. 25 0
      lib/modding/ModUtility.h
  73. 0 1
      lib/registerTypes/RegisterTypes.h
  74. 0 1
      lib/registerTypes/TypesClientPacks1.cpp
  75. 0 1
      lib/registerTypes/TypesClientPacks2.cpp
  76. 0 1
      lib/registerTypes/TypesLobbyPacks.cpp
  77. 0 1
      lib/registerTypes/TypesMapObjects1.cpp
  78. 0 1
      lib/registerTypes/TypesMapObjects2.cpp
  79. 0 1
      lib/registerTypes/TypesMapObjects3.cpp
  80. 0 1
      lib/registerTypes/TypesServerPacks.cpp
  81. 3 3
      lib/rewardable/Info.cpp
  82. 2 2
      lib/rmg/CRmgTemplate.cpp
  83. 0 1
      lib/rmg/CRmgTemplateStorage.cpp
  84. 3 2
      lib/rmg/modificators/RoadPlacer.cpp
  85. 2 2
      lib/serializer/JsonSerializeFormat.h
  86. 5 4
      lib/spells/CSpellHandler.cpp
  87. 5 4
      lib/spells/TargetCondition.cpp
  88. 0 1
      mapeditor/graphics.cpp
  89. 0 1
      mapeditor/inspector/townbulidingswidget.cpp
  90. 4 4
      mapeditor/mainwindow.cpp
  91. 2 1
      mapeditor/mapcontroller.cpp
  92. 1 1
      mapeditor/mapcontroller.h
  93. 0 1
      mapeditor/maphandler.cpp
  94. 2 1
      mapeditor/mapsettings.cpp
  95. 2 1
      mapeditor/validator.cpp
  96. 3 3
      scripting/lua/LuaScriptingContext.cpp
  97. 3 3
      server/CGameHandler.cpp
  98. 0 1
      server/CVCMIServer.cpp
  99. 3 2
      server/PlayerMessageProcessor.cpp
  100. 2 2
      test/scripting/ScriptFixture.cpp

+ 6 - 3
client/ClientCommandManager.cpp

@@ -31,8 +31,10 @@
 #include "../CCallback.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/filesystem/Filesystem.h"
+#include "../lib/modding/CModHandler.h"
+#include "../lib/modding/ContentTypeHandler.h"
+#include "../lib/modding/ModUtility.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CModHandler.h"
 #include "../lib/VCMIDirs.h"
 #include "CMT.h"
 
@@ -229,7 +231,8 @@ void ClientCommandManager::handleGetConfigCommand()
 
 	for(auto contentName : contentNames)
 	{
-		auto& content = (*VLC->modh->content)[contentName];
+		auto const & handler = *VLC->modh->content;
+		auto const & content = handler[contentName];
 
 		auto contentOutPath = outPath / contentName;
 		boost::filesystem::create_directories(contentOutPath);
@@ -242,7 +245,7 @@ void ClientCommandManager::handleGetConfigCommand()
 			{
 				const JsonNode& object = nameAndObject.second;
 
-				std::string name = CModHandler::makeFullIdentifier(object.meta, contentName, nameAndObject.first);
+				std::string name = ModUtility::makeFullIdentifier(object.meta, contentName, nameAndObject.first);
 
 				boost::algorithm::replace_all(name, ":", "_");
 

+ 0 - 1
client/adventureMap/CList.cpp

@@ -25,7 +25,6 @@
 
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CHeroHandler.h"
-#include "../../lib/CModHandler.h"
 #include "../../lib/GameSettings.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"

+ 2 - 2
client/lobby/CLobbyScreen.cpp

@@ -24,11 +24,11 @@
 #include "../../CCallback.h"
 
 #include "../CGameInfo.h"
-#include "../../lib/CModHandler.h"
 #include "../../lib/NetPacksLobby.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/campaign/CampaignHandler.h"
 #include "../../lib/mapping/CMapInfo.h"
+#include "../../lib/modding/ModIncompatibility.h"
 #include "../../lib/rmg/CMapGenOptions.h"
 
 CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
@@ -131,7 +131,7 @@ void CLobbyScreen::startScenario(bool allowOnlyAI)
 		CSH->sendStartGame(allowOnlyAI);
 		buttonStart->block(true);
 	}
-	catch(CModHandler::Incompatibility & e)
+	catch(ModIncompatibility & e)
 	{
 		logGlobal->warn("Incompatibility exception during start scenario: %s", e.what());
 		

+ 0 - 1
client/lobby/RandomMapTab.cpp

@@ -31,7 +31,6 @@
 #include "../../lib/mapping/CMapHeader.h"
 #include "../../lib/mapping/MapFormat.h"
 #include "../../lib/rmg/CMapGenOptions.h"
-#include "../../lib/CModHandler.h"
 #include "../../lib/rmg/CRmgTemplateStorage.h"
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/RoadHandler.h"

+ 0 - 1
client/lobby/SelectionTab.cpp

@@ -33,7 +33,6 @@
 #include "../../lib/NetPacksLobby.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CConfigHandler.h"
-#include "../../lib/CModHandler.h"
 #include "../../lib/GameSettings.h"
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/campaign/CampaignState.h"

+ 3 - 2
client/render/Graphics.cpp

@@ -27,7 +27,8 @@
 
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/filesystem/CBinaryReader.h"
-#include "../lib/CModHandler.h"
+#include "../lib/modding/CModHandler.h"
+#include "../lib/modding/ModScope.h"
 #include "CGameInfo.h"
 #include "../lib/VCMI_Lib.h"
 #include "../CCallback.h"
@@ -100,7 +101,7 @@ void Graphics::loadPaletteAndColors()
 void Graphics::initializeBattleGraphics()
 {
 	auto allConfigs = VLC->modh->getActiveMods();
-	allConfigs.insert(allConfigs.begin(), CModHandler::scopeBuiltin());
+	allConfigs.insert(allConfigs.begin(), ModScope::scopeBuiltin());
 	for(auto & mod : allConfigs)
 	{
 		if(!CResourceHandler::get(mod)->existsResource(ResourceID("config/battles_graphics.json")))

+ 1 - 1
client/renderSDL/CBitmapFont.cpp

@@ -14,11 +14,11 @@
 #include "../CGameInfo.h"
 #include "../render/Colors.h"
 
-#include "../../lib/CModHandler.h"
 #include "../../lib/Languages.h"
 #include "../../lib/Rect.h"
 #include "../../lib/TextOperations.h"
 #include "../../lib/filesystem/Filesystem.h"
+#include "../../lib/modding/CModHandler.h"
 #include "../../lib/vcmi_endian.h"
 #include "../../lib/VCMI_Lib.h"
 

+ 0 - 1
client/widgets/MiscWidgets.cpp

@@ -27,7 +27,6 @@
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/gameState/InfoAboutArmy.h"
 #include "../../lib/CGeneralTextHandler.h"
-#include "../../lib/CModHandler.h"
 #include "../../lib/GameSettings.h"
 #include "../../lib/TextOperations.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"

+ 0 - 1
client/windows/CCastleInterface.cpp

@@ -40,7 +40,6 @@
 #include "../../lib/CBuildingHandler.h"
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
-#include "../../lib/CModHandler.h"
 #include "../../lib/GameSettings.h"
 #include "../../lib/spells/CSpellHandler.h"
 #include "../../lib/CTownHandler.h"

+ 0 - 1
client/windows/CCreatureWindow.cpp

@@ -29,7 +29,6 @@
 #include "../../lib/CStack.h"
 #include "../../lib/CBonusTypeHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
-#include "../../lib/CModHandler.h"
 #include "../../lib/GameSettings.h"
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/gameState/CGameState.h"

+ 0 - 1
client/windows/CKingdomInterface.cpp

@@ -32,7 +32,6 @@
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/CHeroHandler.h"
-#include "../../lib/CModHandler.h"
 #include "../../lib/GameSettings.h"
 #include "../../lib/CSkillHandler.h"
 #include "../../lib/CTownHandler.h"

+ 0 - 1
client/windows/GUIClasses.cpp

@@ -63,7 +63,6 @@
 #include "../lib/CCreatureHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CModHandler.h"
 #include "../lib/GameSettings.h"
 #include "../lib/CondSh.h"
 #include "../lib/CSkillHandler.h"

+ 10 - 0
cmake_modules/VCMI_lib.cmake

@@ -116,7 +116,11 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/mapping/ObstacleProxy.cpp
 
 		${MAIN_LIB_DIR}/modding/CModHandler.cpp
+		${MAIN_LIB_DIR}/modding/CModInfo.cpp
 		${MAIN_LIB_DIR}/modding/CModVersion.cpp
+		${MAIN_LIB_DIR}/modding/ContentTypeHandler.cpp
+		${MAIN_LIB_DIR}/modding/IdentifierStorage.cpp
+		${MAIN_LIB_DIR}/modding/ModUtility.cpp
 
 		${MAIN_LIB_DIR}/pathfinder/CGPathNode.cpp
 		${MAIN_LIB_DIR}/pathfinder/CPathfinder.cpp
@@ -452,7 +456,13 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/mapping/ObstacleProxy.h
 
 		${MAIN_LIB_DIR}/modding/CModHandler.h
+		${MAIN_LIB_DIR}/modding/CModInfo.h
 		${MAIN_LIB_DIR}/modding/CModVersion.h
+		${MAIN_LIB_DIR}/modding/ContentTypeHandler.h
+		${MAIN_LIB_DIR}/modding/IdentifierStorage.h
+		${MAIN_LIB_DIR}/modding/ModIncompatibility.h
+		${MAIN_LIB_DIR}/modding/ModScope.h
+		${MAIN_LIB_DIR}/modding/ModUtility.h
 
 		${MAIN_LIB_DIR}/pathfinder/INodeStorage.h
 		${MAIN_LIB_DIR}/pathfinder/CGPathNode.h

+ 3 - 1
launcher/modManager/cmodmanager.cpp

@@ -13,7 +13,9 @@
 #include "../../lib/VCMIDirs.h"
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/filesystem/CZipLoader.h"
-#include "../../lib/CModHandler.h"
+#include "../../lib/modding/CModHandler.h"
+#include "../../lib/modding/CModInfo.h"
+#include "../../lib/modding/IdentifierStorage.h"
 
 #include "../jsonutils.h"
 #include "../launcherdirs.h"

+ 3 - 4
lib/CArtHandler.cpp

@@ -13,7 +13,6 @@
 
 #include "ArtifactUtils.h"
 #include "CGeneralTextHandler.h"
-#include "CModHandler.h"
 #include "GameSettings.h"
 #include "mapObjects/MapObjects.h"
 #include "StringConstants.h"
@@ -450,7 +449,7 @@ CArtifact * CArtHandler::loadFromJson(const std::string & scope, const JsonNode
 	const JsonNode & warMachine = node["warMachine"];
 	if(warMachine.getType() == JsonNode::JsonType::DATA_STRING && !warMachine.String().empty())
 	{
-		VLC->modh->identifiers.requestIdentifier("creature", warMachine, [=](si32 id)
+		VLC->identifiers()->requestIdentifier("creature", warMachine, [=](si32 id)
 		{
 			art->warMachine = CreatureID(id);
 
@@ -459,7 +458,7 @@ CArtifact * CArtHandler::loadFromJson(const std::string & scope, const JsonNode
 		});
 	}
 
-	VLC->modh->identifiers.requestIdentifier(scope, "object", "artifact", [=](si32 index)
+	VLC->identifiers()->requestIdentifier(scope, "object", "artifact", [=](si32 index)
 	{
 		JsonNode conf;
 		conf.setMeta(scope);
@@ -599,7 +598,7 @@ void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node)
 	{
 		for(const auto & component : node["components"].Vector())
 		{
-			VLC->modh->identifiers.requestIdentifier("artifact", component, [=](si32 id)
+			VLC->identifiers()->requestIdentifier("artifact", component, [=](si32 id)
 			{
 				// when this code is called both combinational art as well as component are loaded
 				// so it is safe to access any of them

+ 5 - 5
lib/CCreatureHandler.cpp

@@ -15,7 +15,6 @@
 #include "filesystem/Filesystem.h"
 #include "VCMI_Lib.h"
 #include "CTownHandler.h"
-#include "CModHandler.h"
 #include "GameSettings.h"
 #include "StringConstants.h"
 #include "bonuses/Limiters.h"
@@ -24,6 +23,7 @@
 #include "serializer/JsonUpdater.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
+#include "modding/CModHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -405,7 +405,7 @@ CCreatureHandler::CCreatureHandler()
 
 const CCreature * CCreatureHandler::getCreature(const std::string & scope, const std::string & identifier) const
 {
-	std::optional<si32> index = VLC->modh->identifiers.getIdentifier(scope, "creature", identifier);
+	std::optional<si32> index = VLC->identifiers()->getIdentifier(scope, "creature", identifier);
 
 	if(!index)
 		throw std::runtime_error("Creature not found "+identifier);
@@ -627,7 +627,7 @@ CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const Json
 	JsonNode advMapFile = node["graphics"]["map"];
 	JsonNode advMapMask = node["graphics"]["mapMask"];
 
-	VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index)
+	VLC->identifiers()->requestIdentifier(scope, "object", "monster", [=](si32 index)
 	{
 		JsonNode conf;
 		conf.setMeta(scope);
@@ -920,14 +920,14 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
 		}
 	}
 
-	VLC->modh->identifiers.requestIdentifier("faction", config["faction"], [=](si32 faction)
+	VLC->identifiers()->requestIdentifier("faction", config["faction"], [=](si32 faction)
 	{
 		creature->faction = FactionID(faction);
 	});
 
 	for(const JsonNode &value : config["upgrades"].Vector())
 	{
-		VLC->modh->identifiers.requestIdentifier("creature", value, [=](si32 identifier)
+		VLC->identifiers()->requestIdentifier("creature", value, [=](si32 identifier)
 		{
 			creature->upgrades.insert(CreatureID(identifier));
 		});

+ 2 - 2
lib/CCreatureSet.cpp

@@ -14,9 +14,9 @@
 #include "CConfigHandler.h"
 #include "CCreatureHandler.h"
 #include "VCMI_Lib.h"
-#include "CModHandler.h"
 #include "GameSettings.h"
 #include "mapObjects/CGHeroInstance.h"
+#include "modding/ModScope.h"
 #include "IGameCallback.h"
 #include "CGeneralTextHandler.h"
 #include "spells/CSpellHandler.h"
@@ -1046,7 +1046,7 @@ void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler)
 		std::string typeName;
 		handler.serializeString("type", typeName);
 		if(!typeName.empty())
-			setType(VLC->creh->getCreature(CModHandler::scopeMap(), typeName));
+			setType(VLC->creh->getCreature(ModScope::scopeMap(), typeName));
 	}
 }
 

+ 0 - 1
lib/CGameInfoCallback.cpp

@@ -18,7 +18,6 @@
 #include "StartInfo.h" // for StartInfo
 #include "battle/BattleInfo.h" // for BattleInfo
 #include "NetPacks.h" // for InfoWindow
-#include "CModHandler.h"
 #include "GameSettings.h"
 #include "TerrainHandler.h"
 #include "spells/CSpellHandler.h"

+ 1 - 1
lib/CGeneralTextHandler.cpp

@@ -12,9 +12,9 @@
 
 #include "filesystem/Filesystem.h"
 #include "CConfigHandler.h"
-#include "CModHandler.h"
 #include "GameSettings.h"
 #include "mapObjects/CQuest.h"
+#include "modding/CModHandler.h"
 #include "VCMI_Lib.h"
 #include "Languages.h"
 #include "TextOperations.h"

+ 11 - 11
lib/CHeroHandler.cpp

@@ -18,7 +18,6 @@
 #include "battle/BattleHex.h"
 #include "CCreatureHandler.h"
 #include "GameSettings.h"
-#include "CModHandler.h"
 #include "CTownHandler.h"
 #include "CSkillHandler.h"
 #include "BattleFieldHandler.h"
@@ -26,6 +25,7 @@
 #include "bonuses/Updaters.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
+#include "modding/IdentifierStorage.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -269,7 +269,7 @@ CHeroClass * CHeroClassHandler::loadFromJson(const std::string & scope, const Js
 	for(auto skillPair : node["secondarySkills"].Struct())
 	{
 		int probability = static_cast<int>(skillPair.second.Integer());
-		VLC->modh->identifiers.requestIdentifier(skillPair.second.meta, "skill", skillPair.first, [heroClass, probability](si32 skillID)
+		VLC->identifiers()->requestIdentifier(skillPair.second.meta, "skill", skillPair.first, [heroClass, probability](si32 skillID)
 		{
 			if(heroClass->secSkillProbability.size() <= skillID)
 				heroClass->secSkillProbability.resize(skillID + 1, -1); // -1 = override with default later
@@ -277,7 +277,7 @@ CHeroClass * CHeroClassHandler::loadFromJson(const std::string & scope, const Js
 		});
 	}
 
-	VLC->modh->identifiers.requestIdentifier ("creature", node["commander"],
+	VLC->identifiers()->requestIdentifier ("creature", node["commander"],
 	[=](si32 commanderID)
 	{
 		heroClass->commander = VLC->creh->objects[commanderID];
@@ -288,20 +288,20 @@ CHeroClass * CHeroClassHandler::loadFromJson(const std::string & scope, const Js
 	{
 		int value = static_cast<int>(tavern.second.Float());
 
-		VLC->modh->identifiers.requestIdentifier(tavern.second.meta, "faction", tavern.first,
+		VLC->identifiers()->requestIdentifier(tavern.second.meta, "faction", tavern.first,
 		[=](si32 factionID)
 		{
 			heroClass->selectionProbability[FactionID(factionID)] = value;
 		});
 	}
 
-	VLC->modh->identifiers.requestIdentifier("faction", node["faction"],
+	VLC->identifiers()->requestIdentifier("faction", node["faction"],
 	[=](si32 factionID)
 	{
 		heroClass->faction = factionID;
 	});
 
-	VLC->modh->identifiers.requestIdentifier(scope, "object", "hero", [=](si32 index)
+	VLC->identifiers()->requestIdentifier(scope, "object", "hero", [=](si32 index)
 	{
 		JsonNode classConf = node["mapObject"];
 		classConf["heroClass"].String() = identifier;
@@ -444,7 +444,7 @@ CHero * CHeroHandler::loadFromJson(const std::string & scope, const JsonNode & n
 	loadHeroSkills(hero, node);
 	loadHeroSpecialty(hero, node);
 
-	VLC->modh->identifiers.requestIdentifier("heroClass", node["class"],
+	VLC->identifiers()->requestIdentifier("heroClass", node["class"],
 	[=](si32 classID)
 	{
 		hero->heroClass = classes[HeroClassID(classID)];
@@ -468,7 +468,7 @@ void CHeroHandler::loadHeroArmy(CHero * hero, const JsonNode & node) const
 
 		assert(hero->initialArmy[i].minAmount <= hero->initialArmy[i].maxAmount);
 
-		VLC->modh->identifiers.requestIdentifier("creature", source["creature"], [=](si32 creature)
+		VLC->identifiers()->requestIdentifier("creature", source["creature"], [=](si32 creature)
 		{
 			hero->initialArmy[i].creature = CreatureID(creature);
 		});
@@ -485,7 +485,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node) const
 			size_t currentIndex = hero->secSkillsInit.size();
 			hero->secSkillsInit.emplace_back(SecondarySkill(-1), skillLevel);
 
-			VLC->modh->identifiers.requestIdentifier("skill", set["skill"], [=](si32 id)
+			VLC->identifiers()->requestIdentifier("skill", set["skill"], [=](si32 id)
 			{
 				hero->secSkillsInit[currentIndex].first = SecondarySkill(id);
 			});
@@ -501,7 +501,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node) const
 
 	for(const JsonNode & spell : node["spellbook"].Vector())
 	{
-		VLC->modh->identifiers.requestIdentifier("spell", spell,
+		VLC->identifiers()->requestIdentifier("spell", spell,
 		[=](si32 spellID)
 		{
 			hero->spells.insert(SpellID(spellID));
@@ -624,7 +624,7 @@ void CHeroHandler::loadHeroSpecialty(CHero * hero, const JsonNode & node)
 
 		std::function<void()> specialtyLoader = [creatureNode, hero, prepSpec]
 		{
-			VLC->modh->identifiers.requestIdentifier("creature", creatureNode, [hero, prepSpec](si32 creature)
+			VLC->identifiers()->requestIdentifier("creature", creatureNode, [hero, prepSpec](si32 creature)
 			{
 				for (const auto & bonus : createCreatureSpecialty(CreatureID(creature)))
 					hero->specialty.push_back(prepSpec(bonus));

+ 5 - 3
lib/CSkillHandler.cpp

@@ -16,10 +16,12 @@
 
 #include "CGeneralTextHandler.h"
 #include "filesystem/Filesystem.h"
+#include "modding/IdentifierStorage.h"
+#include "modding/ModUtility.h"
+#include "modding/ModScope.h"
 
 #include "JsonNode.h"
 
-#include "CModHandler.h"
 #include "StringConstants.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -262,7 +264,7 @@ std::vector<bool> CSkillHandler::getDefaultAllowed() const
 
 si32 CSkillHandler::decodeSkill(const std::string & identifier)
 {
-	auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "skill", identifier);
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "skill", identifier);
 	if(rawId)
 		return rawId.value();
 	else
@@ -276,7 +278,7 @@ std::string CSkillHandler::encodeSkill(const si32 index)
 
 std::string CSkillHandler::encodeSkillWithType(const si32 index)
 {
-	return CModHandler::makeFullIdentifier("", "skill", encodeSkill(index));
+	return ModUtility::makeFullIdentifier("", "skill", encodeSkill(index));
 }
 
 VCMI_LIB_NAMESPACE_END

+ 21 - 20
lib/CTownHandler.cpp

@@ -15,7 +15,6 @@
 #include "JsonNode.h"
 #include "StringConstants.h"
 #include "CCreatureHandler.h"
-#include "CModHandler.h"
 #include "CHeroHandler.h"
 #include "CArtHandler.h"
 #include "GameSettings.h"
@@ -27,6 +26,8 @@
 #include "ResourceSet.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
+#include "modding/IdentifierStorage.h"
+#include "modding/ModScope.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -689,7 +690,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
 												stringID % ret->town->faction->getNameTranslated()));
 		}
 
-		VLC->modh->identifiers.requestIdentifier(ret->town->getBuildingScope(), source["upgrades"], [=](si32 identifier)
+		VLC->identifiers()->requestIdentifier(ret->town->getBuildingScope(), source["upgrades"], [=](si32 identifier)
 		{
 			ret->upgrade = BuildingID(identifier);
 		});
@@ -721,21 +722,21 @@ void CTownHandler::loadStructure(CTown &town, const std::string & stringID, cons
 	ret->building = nullptr;
 	ret->buildable = nullptr;
 
-	VLC->modh->identifiers.tryRequestIdentifier( source.meta, "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
+	VLC->identifiers()->tryRequestIdentifier( source.meta, "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
 	{
 		ret->building = town.buildings[BuildingID(identifier)];
 	});
 
 	if (source["builds"].isNull())
 	{
-		VLC->modh->identifiers.tryRequestIdentifier( source.meta, "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
+		VLC->identifiers()->tryRequestIdentifier( source.meta, "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
 		{
 			ret->building = town.buildings[BuildingID(identifier)];
 		});
 	}
 	else
 	{
-		VLC->modh->identifiers.requestIdentifier("building." + town.faction->getJsonKey(), source["builds"], [=, &town](si32 identifier) mutable
+		VLC->identifiers()->requestIdentifier("building." + town.faction->getJsonKey(), source["builds"], [=, &town](si32 identifier) mutable
 		{
 			ret->buildable = town.buildings[BuildingID(identifier)];
 		});
@@ -786,7 +787,7 @@ void CTownHandler::loadTownHall(CTown &town, const JsonNode & source) const
 				auto & dst = dstBox[k];
 				const auto & src = srcBox[k];
 
-				VLC->modh->identifiers.requestIdentifier("building." + town.faction->getJsonKey(), src, [&](si32 identifier)
+				VLC->identifiers()->requestIdentifier("building." + town.faction->getJsonKey(), src, [&](si32 identifier)
 				{
 					dst = BuildingID(identifier);
 				});
@@ -812,7 +813,7 @@ void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source) const
 	town.clientInfo.towerIconSmall = source["towerIconSmall"].String();
 	town.clientInfo.towerIconLarge = source["towerIconLarge"].String();
 
-	VLC->modh->identifiers.requestIdentifier("creature", source["shooter"], [&town](si32 creature)
+	VLC->identifiers()->requestIdentifier("creature", source["shooter"], [&town](si32 creature)
 	{
 		auto crId = CreatureID(creature);
 		if((*VLC->creh)[crId]->animation.missleFrameAngles.empty())
@@ -915,14 +916,14 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
 
 	if (!source["moatAbility"].isNull()) // VCMI 1.2 compatibility code
 	{
-		VLC->modh->identifiers.requestIdentifier( "spell", source["moatAbility"], [=](si32 ability)
+		VLC->identifiers()->requestIdentifier( "spell", source["moatAbility"], [=](si32 ability)
 		{
 			town->moatAbility = SpellID(ability);
 		});
 	}
 	else
 	{
-		VLC->modh->identifiers.requestIdentifier( source.meta, "spell", "castleMoat", [=](si32 ability)
+		VLC->identifiers()->requestIdentifier( source.meta, "spell", "castleMoat", [=](si32 ability)
 		{
 			town->moatAbility = SpellID(ability);
 		});
@@ -949,7 +950,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
 
 		for (size_t j=0; j<level.size(); j++)
 		{
-			VLC->modh->identifiers.requestIdentifier("creature", level[j], [=](si32 creature)
+			VLC->identifiers()->requestIdentifier("creature", level[j], [=](si32 creature)
 			{
 				town->creatures[i][j] = CreatureID(creature);
 			});
@@ -962,7 +963,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
 	{
 		int chance = static_cast<int>(node.second.Float());
 
-		VLC->modh->identifiers.requestIdentifier(node.second.meta, "heroClass",node.first, [=](si32 classID)
+		VLC->identifiers()->requestIdentifier(node.second.meta, "heroClass",node.first, [=](si32 classID)
 		{
 			VLC->heroh->classes[HeroClassID(classID)]->selectionProbability[town->faction->getId()] = chance;
 		});
@@ -972,7 +973,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
 	{
 		int chance = static_cast<int>(node.second.Float());
 
-		VLC->modh->identifiers.requestIdentifier(node.second.meta, "spell", node.first, [=](si32 spellID)
+		VLC->identifiers()->requestIdentifier(node.second.meta, "spell", node.first, [=](si32 spellID)
 		{
 			VLC->spellh->objects.at(spellID)->probabilities[town->faction->getId()] = chance;
 		});
@@ -1032,7 +1033,7 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
 	faction->boatType = EBoatId::NONE;
 	if (!source["boat"].isNull())
 	{
-		VLC->modh->identifiers.requestIdentifier("core:boat", source["boat"], [=](int32_t boatTypeID)
+		VLC->identifiers()->requestIdentifier("core:boat", source["boat"], [=](int32_t boatTypeID)
 		{
 			faction->boatType = BoatId(boatTypeID);
 		});
@@ -1054,7 +1055,7 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
 	faction->nativeTerrain = ETerrainId::NONE;
 	if ( !source["nativeTerrain"].isNull() && source["nativeTerrain"].String() != "none")
 	{
-		VLC->modh->identifiers.requestIdentifier("terrain", source["nativeTerrain"], [=](int32_t index){
+		VLC->identifiers()->requestIdentifier("terrain", source["nativeTerrain"], [=](int32_t index){
 			faction->nativeTerrain = TerrainId(index);
 
 			auto const & terrain = VLC->terrainTypeHandler->getById(faction->nativeTerrain);
@@ -1093,7 +1094,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 		info.icons[1][0] = 8 + object->index * 4 + 2;
 		info.icons[1][1] = 8 + object->index * 4 + 3;
 
-		VLC->modh->identifiers.requestIdentifier(scope, "object", "town", [=](si32 index)
+		VLC->identifiers()->requestIdentifier(scope, "object", "town", [=](si32 index)
 		{
 			// register town once objects are loaded
 			JsonNode config = data["town"]["mapObject"];
@@ -1136,7 +1137,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 		info.icons[1][0] = object->index * 2 + 0;
 		info.icons[1][1] = object->index * 2 + 1;
 
-		VLC->modh->identifiers.requestIdentifier(scope, "object", "town", [=](si32 index)
+		VLC->identifiers()->requestIdentifier(scope, "object", "town", [=](si32 index)
 		{
 			// register town once objects are loaded
 			JsonNode config = data["town"]["mapObject"];
@@ -1154,7 +1155,7 @@ void CTownHandler::loadRandomFaction()
 	static const ResourceID randomFactionPath("config/factions/random.json");
 
 	JsonNode randomFactionJson(randomFactionPath);
-	randomFactionJson.setMeta(CModHandler::scopeBuiltin(), true);
+	randomFactionJson.setMeta(ModScope::scopeBuiltin(), true);
 	loadBuildings(randomTown, randomFactionJson["random"]["town"]["buildings"]);
 }
 
@@ -1183,7 +1184,7 @@ void CTownHandler::initializeRequirements()
 				logMod->warn("Entry contains: ");
 				logMod->warn(node.toJson());
 			}
-			return BuildingID(VLC->modh->identifiers.getIdentifier(requirement.town->getBuildingScope(), node.Vector()[0]).value());
+			return BuildingID(VLC->identifiers()->getIdentifier(requirement.town->getBuildingScope(), node.Vector()[0]).value());
 		});
 	}
 	requirementsToLoad.clear();
@@ -1198,7 +1199,7 @@ void CTownHandler::initializeOverridden()
 
 		for(const auto & b : jsonNode.Vector())
 		{
-			auto bid = BuildingID(VLC->modh->identifiers.getIdentifier(scope, b).value());
+			auto bid = BuildingID(VLC->identifiers()->getIdentifier(scope, b).value());
 			bidHelper.building->overrideBids.insert(bid);
 		}
 	}
@@ -1213,7 +1214,7 @@ void CTownHandler::initializeWarMachines()
 		CTown * t = p.first;
 		JsonNode creatureKey = p.second;
 
-		auto ret = VLC->modh->identifiers.getIdentifier("creature", creatureKey, false);
+		auto ret = VLC->identifiers()->getIdentifier("creature", creatureKey, false);
 
 		if(ret)
 		{

+ 8 - 7
lib/GameConstants.cpp

@@ -25,6 +25,8 @@
 #include <vcmi/spells/Spell.h>
 #include <vcmi/spells/Service.h>
 
+#include "modding/IdentifierStorage.h"
+#include "modding/ModScope.h"
 #include "VCMI_Lib.h"
 #include "CArtHandler.h"//todo: remove
 #include "CCreatureHandler.h"//todo: remove
@@ -32,7 +34,6 @@
 #include "CSkillHandler.h"//todo: remove
 #include "StringConstants.h"
 #include "CGeneralTextHandler.h"
-#include "CModHandler.h"//todo: remove
 #include "TerrainHandler.h" //TODO: remove
 #include "BattleFieldHandler.h"
 #include "ObstacleHandler.h"
@@ -65,7 +66,7 @@ namespace GameConstants
 
 si32 HeroTypeID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "hero", identifier);
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", identifier);
 	if(rawId)
 		return rawId.value();
 	else
@@ -89,7 +90,7 @@ const Artifact * ArtifactID::toArtifact(const ArtifactService * service) const
 
 si32 ArtifactID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "artifact", identifier);
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "artifact", identifier);
 	if(rawId)
 		return rawId.value();
 	else
@@ -113,7 +114,7 @@ const Creature * CreatureID::toCreature(const CreatureService * creatures) const
 
 si32 CreatureID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "creature", identifier);
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "creature", identifier);
 	if(rawId)
 		return rawId.value();
 	else
@@ -142,7 +143,7 @@ const spells::Spell * SpellID::toSpell(const spells::Service * service) const
 
 si32 SpellID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "spell", identifier);
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "spell", identifier);
 	if(rawId)
 		return rawId.value();
 	else
@@ -205,7 +206,7 @@ const FactionID FactionID::NEUTRAL = FactionID(9);
 
 si32 FactionID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), entityType(), identifier);
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier);
 	if(rawId)
 		return rawId.value();
 	else
@@ -225,7 +226,7 @@ std::string FactionID::entityType()
 
 si32 TerrainID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), entityType(), identifier);
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier);
 	if(rawId)
 		return rawId.value();
 	else

+ 4 - 1
lib/IGameCallback.cpp

@@ -15,7 +15,6 @@
 #include "CSkillHandler.h"// for CSkill
 #include "NetPacks.h"
 #include "CBonusTypeHandler.h"
-#include "CModHandler.h"
 #include "BattleFieldHandler.h"
 #include "ObstacleHandler.h"
 #include "bonuses/CBonusSystemNode.h"
@@ -38,6 +37,10 @@
 #include "gameState/CGameStateCampaign.h"
 #include "gameState/TavernHeroesPool.h"
 #include "mapping/CMap.h"
+#include "modding/CModHandler.h"
+#include "modding/CModInfo.h"
+#include "modding/IdentifierStorage.h"
+#include "modding/CModVersion.h"
 #include "CPlayerState.h"
 #include "GameSettings.h"
 #include "ScriptHandler.h"

+ 5 - 3
lib/IHandlerBase.cpp

@@ -10,18 +10,20 @@
 
 #include "StdInc.h"
 #include "IHandlerBase.h"
-#include "CModHandler.h"
+#include "modding/IdentifierStorage.h"
+#include "modding/ModScope.h"
+#include "modding/CModHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
 std::string IHandlerBase::getScopeBuiltin()
 {
-	return CModHandler::scopeBuiltin();
+	return ModScope::scopeBuiltin();
 }
 
 void IHandlerBase::registerObject(const std::string & scope, const std::string & type_name, const std::string & name, si32 index)
 {
-	return VLC->modh->identifiers.registerObject(scope, type_name, name, index);
+	return VLC->modh->getIdentifiers().registerObject(scope, type_name, name, index);
 }
 
 VCMI_LIB_NAMESPACE_END

+ 4 - 3
lib/JsonDetail.cpp

@@ -13,9 +13,10 @@
 
 #include "VCMI_Lib.h"
 #include "TextOperations.h"
-#include "CModHandler.h"
 
 #include "filesystem/Filesystem.h"
+#include "modding/ModScope.h"
+#include "modding/CModHandler.h"
 #include "ScopeGuard.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -1007,7 +1008,7 @@ namespace
 		bool testFilePresence(const std::string & scope, const ResourceID & resource)
 		{
 			std::set<std::string> allowedScopes;
-			if(scope != CModHandler::scopeBuiltin() && !scope.empty()) // all real mods may have dependencies
+			if(scope != ModScope::scopeBuiltin() && !scope.empty()) // all real mods may have dependencies
 			{
 				//NOTE: recursive dependencies are not allowed at the moment - update code if this changes
 				bool found = true;
@@ -1016,7 +1017,7 @@ namespace
 				if(!found)
 					return false;
 
-				allowedScopes.insert(CModHandler::scopeBuiltin()); // all mods can use H3 files
+				allowedScopes.insert(ModScope::scopeBuiltin()); // all mods can use H3 files
 			}
 			allowedScopes.insert(scope); // mods can use their own files
 

+ 8 - 8
lib/JsonNode.cpp

@@ -19,8 +19,8 @@
 #include "bonuses/Propagators.h"
 #include "bonuses/Updaters.h"
 #include "filesystem/Filesystem.h"
+#include "modding/IdentifierStorage.h"
 #include "VCMI_Lib.h" //for identifier resolution
-#include "CModHandler.h"
 #include "CGeneralTextHandler.h"
 #include "JsonDetail.h"
 #include "StringConstants.h"
@@ -565,7 +565,7 @@ void JsonUtils::resolveIdentifier(si32 & var, const JsonNode & node, const std::
 				var = static_cast<si32>(value.Float());
 				break;
 			case JsonNode::JsonType::DATA_STRING:
-				VLC->modh->identifiers.requestIdentifier(value, [&](si32 identifier)
+				VLC->identifiers()->requestIdentifier(value, [&](si32 identifier)
 				{
 					var = identifier;
 				});
@@ -590,7 +590,7 @@ void JsonUtils::resolveAddInfo(CAddInfo & var, const JsonNode & node)
 			var = static_cast<si32>(value.Float());
 			break;
 		case JsonNode::JsonType::DATA_STRING:
-			VLC->modh->identifiers.requestIdentifier(value, [&](si32 identifier)
+			VLC->identifiers()->requestIdentifier(value, [&](si32 identifier)
 			{
 				var = identifier;
 			});
@@ -610,7 +610,7 @@ void JsonUtils::resolveAddInfo(CAddInfo & var, const JsonNode & node)
 							var[i] = static_cast<si32>(vec[i].Float());
 							break;
 						case JsonNode::JsonType::DATA_STRING:
-							VLC->modh->identifiers.requestIdentifier(vec[i], [&var,i](si32 identifier)
+							VLC->identifiers()->requestIdentifier(vec[i], [&var,i](si32 identifier)
 							{
 								var[i] = identifier;
 							});
@@ -638,7 +638,7 @@ void JsonUtils::resolveIdentifier(const JsonNode &node, si32 &var)
 			var = static_cast<si32>(node.Float());
 			break;
 		case JsonNode::JsonType::DATA_STRING:
-			VLC->modh->identifiers.requestIdentifier(node, [&](si32 identifier)
+			VLC->identifiers()->requestIdentifier(node, [&](si32 identifier)
 			{
 				var = identifier;
 			});
@@ -699,7 +699,7 @@ std::shared_ptr<ILimiter> JsonUtils::parseLimiter(const JsonNode & limiter)
 			if(limiterType == "CREATURE_TYPE_LIMITER")
 			{
 				std::shared_ptr<CCreatureTypeLimiter> creatureLimiter = std::make_shared<CCreatureTypeLimiter>();
-				VLC->modh->identifiers.requestIdentifier("creature", parameters[0], [=](si32 creature)
+				VLC->identifiers()->requestIdentifier("creature", parameters[0], [=](si32 creature)
 				{
 					creatureLimiter->setCreature(CreatureID(creature));
 				});
@@ -771,7 +771,7 @@ std::shared_ptr<ILimiter> JsonUtils::parseLimiter(const JsonNode & limiter)
 			else if(limiterType == "FACTION_LIMITER" || limiterType == "CREATURE_FACTION_LIMITER") //Second name is deprecated, 1.2 compat
 			{
 				std::shared_ptr<FactionLimiter> factionLimiter = std::make_shared<FactionLimiter>();
-				VLC->modh->identifiers.requestIdentifier("faction", parameters[0], [=](si32 faction)
+				VLC->identifiers()->requestIdentifier("faction", parameters[0], [=](si32 faction)
 				{
 					factionLimiter->faction = FactionID(faction);
 				});
@@ -793,7 +793,7 @@ std::shared_ptr<ILimiter> JsonUtils::parseLimiter(const JsonNode & limiter)
 				std::shared_ptr<CreatureTerrainLimiter> terrainLimiter = std::make_shared<CreatureTerrainLimiter>();
 				if(!parameters.empty())
 				{
-					VLC->modh->identifiers.requestIdentifier("terrain", parameters[0], [=](si32 terrain)
+					VLC->identifiers()->requestIdentifier("terrain", parameters[0], [=](si32 terrain)
 					{
 						//TODO: support limiters
 						//terrainLimiter->terrainType = terrain;

+ 11 - 10
lib/JsonRandom.cpp

@@ -17,7 +17,6 @@
 #include "CRandomGenerator.h"
 #include "StringConstants.h"
 #include "VCMI_Lib.h"
-#include "CModHandler.h"
 #include "CArtHandler.h"
 #include "CCreatureHandler.h"
 #include "CCreatureSet.h"
@@ -25,6 +24,8 @@
 #include "CSkillHandler.h"
 #include "IGameCallback.h"
 #include "mapObjects/IObjectInterface.h"
+#include "modding/IdentifierStorage.h"
+#include "modding/ModScope.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -105,7 +106,7 @@ namespace JsonRandom
 		
 		std::string resourceName = loadKey(value, rng, defaultResources);
 		si32 resourceAmount = loadValue(value, rng, 0);
-		si32 resourceID(VLC->modh->identifiers.getIdentifier(value.meta, "resource", resourceName).value());
+		si32 resourceID(VLC->identifiers()->getIdentifier(value.meta, "resource", resourceName).value());
 
 		TResources ret;
 		ret[resourceID] = resourceAmount;
@@ -146,7 +147,7 @@ namespace JsonRandom
 		{
 			for(const auto & pair : value.Struct())
 			{
-				SecondarySkill id(VLC->modh->identifiers.getIdentifier(pair.second.meta, "skill", pair.first).value());
+				SecondarySkill id(VLC->identifiers()->getIdentifier(pair.second.meta, "skill", pair.first).value());
 				ret[id] = loadValue(pair.second, rng);
 			}
 		}
@@ -157,7 +158,7 @@ namespace JsonRandom
 			{
 				IObjectInterface::cb->isAllowed(2, skill->getIndex());
 				auto scopeAndName = vstd::splitStringToPair(skill->getJsonKey(), ':');
-				if(scopeAndName.first == CModHandler::scopeBuiltin() || scopeAndName.first == value.meta)
+				if(scopeAndName.first == ModScope::scopeBuiltin() || scopeAndName.first == value.meta)
 					defaultSkills.insert(scopeAndName.second);
 				else
 					defaultSkills.insert(skill->getJsonKey());
@@ -167,7 +168,7 @@ namespace JsonRandom
 			{
 				auto key = loadKey(element, rng, defaultSkills);
 				defaultSkills.erase(key); //avoid dupicates
-				if(auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "skill", key))
+				if(auto identifier = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "skill", key))
 				{
 					SecondarySkill id(identifier.value());
 					ret[id] = loadValue(element, rng);
@@ -180,7 +181,7 @@ namespace JsonRandom
 	ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng)
 	{
 		if (value.getType() == JsonNode::JsonType::DATA_STRING)
-			return ArtifactID(VLC->modh->identifiers.getIdentifier("artifact", value).value());
+			return ArtifactID(VLC->identifiers()->getIdentifier("artifact", value).value());
 
 		std::set<CArtifact::EartClass> allowedClasses;
 		std::set<ArtifactPosition> allowedPositions;
@@ -241,7 +242,7 @@ namespace JsonRandom
 	SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, std::vector<SpellID> spells)
 	{
 		if (value.getType() == JsonNode::JsonType::DATA_STRING)
-			return SpellID(VLC->modh->identifiers.getIdentifier("spell", value).value());
+			return SpellID(VLC->identifiers()->getIdentifier("spell", value).value());
 
 		if (!value["level"].isNull())
 		{
@@ -255,7 +256,7 @@ namespace JsonRandom
 
 		if (!value["school"].isNull())
 		{
-			int32_t schoolID = VLC->modh->identifiers.getIdentifier("spellSchool", value["school"]).value();
+			int32_t schoolID = VLC->identifiers()->getIdentifier("spellSchool", value["school"]).value();
 
 			vstd::erase_if(spells, [=](const SpellID & spell)
 			{
@@ -284,7 +285,7 @@ namespace JsonRandom
 	CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng)
 	{
 		CStackBasicDescriptor stack;
-		stack.type = VLC->creh->objects[VLC->modh->identifiers.getIdentifier("creature", value["type"]).value()];
+		stack.type = VLC->creh->objects[VLC->identifiers()->getIdentifier("creature", value["type"]).value()];
 		stack.count = loadValue(value, rng);
 		if (!value["upgradeChance"].isNull() && !stack.type->upgrades.empty())
 		{
@@ -320,7 +321,7 @@ namespace JsonRandom
 				info.minAmount = static_cast<si32>(node["min"].Float());
 				info.maxAmount = static_cast<si32>(node["max"].Float());
 			}
-			const CCreature * crea = VLC->creh->objects[VLC->modh->identifiers.getIdentifier("creature", node["type"]).value()];
+			const CCreature * crea = VLC->creh->objects[VLC->identifiers()->getIdentifier("creature", node["type"]).value()];
 			info.allowedCreatures.push_back(crea);
 			if (node["upgradeChance"].Float() > 0)
 			{

+ 0 - 1
lib/NetPacksLib.cpp

@@ -14,7 +14,6 @@
 #include "CGeneralTextHandler.h"
 #include "CArtHandler.h"
 #include "CHeroHandler.h"
-#include "CModHandler.h"
 #include "VCMI_Lib.h"
 #include "mapping/CMap.h"
 #include "spells/CSpellHandler.h"

+ 3 - 2
lib/ObstacleHandler.cpp

@@ -10,7 +10,8 @@
 #include "StdInc.h"
 #include "ObstacleHandler.h"
 #include "BattleFieldHandler.h"
-#include "CModHandler.h"
+#include "modding/IdentifierStorage.h"
+#include "JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -94,7 +95,7 @@ ObstacleInfo * ObstacleHandler::loadFromJson(const std::string & scope, const Js
 	info->height = json["height"].Integer();
 	for(const auto & t : json["allowedTerrains"].Vector())
 	{
-		VLC->modh->identifiers.requestIdentifier("terrain", t, [info](int32_t identifier){
+		VLC->identifiers()->requestIdentifier("terrain", t, [info](int32_t identifier){
 			info->allowedTerrains.emplace_back(identifier);
 		});
 	}

+ 1 - 1
lib/RiverHandler.cpp

@@ -10,9 +10,9 @@
 
 #include "StdInc.h"
 #include "RiverHandler.h"
-#include "CModHandler.h"
 #include "CGeneralTextHandler.h"
 #include "GameSettings.h"
+#include "JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
lib/RoadHandler.cpp

@@ -10,9 +10,9 @@
 
 #include "StdInc.h"
 #include "RoadHandler.h"
-#include "CModHandler.h"
 #include "CGeneralTextHandler.h"
 #include "GameSettings.h"
+#include "JsonNode.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 0 - 1
lib/ScriptHandler.cpp

@@ -17,7 +17,6 @@
 
 #include "CGameInterface.h"
 #include "CScriptingModule.h"
-#include "CModHandler.h"
 
 #include "VCMIDirs.h"
 #include "serializer/JsonDeserializer.h"

+ 3 - 3
lib/StartInfo.cpp

@@ -11,13 +11,13 @@
 #include "StartInfo.h"
 
 #include "CGeneralTextHandler.h"
-#include "CModHandler.h"
 #include "VCMI_Lib.h"
 #include "rmg/CMapGenOptions.h"
 #include "mapping/CMapInfo.h"
 #include "campaign/CampaignState.h"
 #include "mapping/CMapHeader.h"
 #include "mapping/CMapService.h"
+#include "modding/ModIncompatibility.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -74,12 +74,12 @@ void LobbyInfo::verifyStateBeforeStart(bool ignoreNoHuman) const
 		throw std::domain_error("ExceptionMapMissing");
 	
 	auto missingMods = CMapService::verifyMapHeaderMods(*mi->mapHeader);
-	CModHandler::Incompatibility::ModList modList;
+	ModIncompatibility::ModList modList;
 	for(const auto & m : missingMods)
 		modList.push_back({m.first, m.second.toString()});
 	
 	if(!modList.empty())
-		throw CModHandler::Incompatibility(std::move(modList));
+		throw ModIncompatibility(std::move(modList));
 
 	//there must be at least one human player before game can be started
 	std::map<PlayerColor, PlayerSettings>::const_iterator i;

+ 6 - 5
lib/TerrainHandler.cpp

@@ -10,9 +10,10 @@
 
 #include "StdInc.h"
 #include "TerrainHandler.h"
-#include "CModHandler.h"
 #include "CGeneralTextHandler.h"
 #include "GameSettings.h"
+#include "JsonNode.h"
+#include "modding/IdentifierStorage.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -67,7 +68,7 @@ TerrainType * TerrainTypeHandler::loadFromJson( const std::string & scope, const
 	info->river = River::NO_RIVER;
 	if(!json["river"].isNull())
 	{
-		VLC->modh->identifiers.requestIdentifier("river", json["river"], [info](int32_t identifier)
+		VLC->identifiers()->requestIdentifier("river", json["river"], [info](int32_t identifier)
 		{
 			info->river = RiverId(identifier);
 		});
@@ -87,7 +88,7 @@ TerrainType * TerrainTypeHandler::loadFromJson( const std::string & scope, const
 
 	for(const auto & t : json["battleFields"].Vector())
 	{
-		VLC->modh->identifiers.requestIdentifier("battlefield", t, [info](int32_t identifier)
+		VLC->identifiers()->requestIdentifier("battlefield", t, [info](int32_t identifier)
 		{
 			info->battleFields.emplace_back(identifier);
 		});
@@ -95,7 +96,7 @@ TerrainType * TerrainTypeHandler::loadFromJson( const std::string & scope, const
 
 	for(const auto & t : json["prohibitTransitions"].Vector())
 	{
-		VLC->modh->identifiers.requestIdentifier("terrain", t, [info](int32_t identifier)
+		VLC->identifiers()->requestIdentifier("terrain", t, [info](int32_t identifier)
 		{
 			info->prohibitTransitions.emplace_back(identifier);
 		});
@@ -105,7 +106,7 @@ TerrainType * TerrainTypeHandler::loadFromJson( const std::string & scope, const
 
 	if(!json["rockTerrain"].isNull())
 	{
-		VLC->modh->identifiers.requestIdentifier("terrain", json["rockTerrain"], [info](int32_t identifier)
+		VLC->identifiers()->requestIdentifier("terrain", json["rockTerrain"], [info](int32_t identifier)
 		{
 			info->rockTerrain = TerrainId(identifier);
 		});

+ 9 - 1
lib/VCMI_Lib.cpp

@@ -25,7 +25,10 @@
 #include "spells/effects/Registry.h"
 #include "CSkillHandler.h"
 #include "CGeneralTextHandler.h"
-#include "CModHandler.h"
+#include "modding/CModHandler.h"
+#include "modding/CModInfo.h"
+#include "modding/IdentifierStorage.h"
+#include "modding/CModVersion.h"
 #include "IGameEventsReceiver.h"
 #include "CStopWatch.h"
 #include "VCMIDirs.h"
@@ -106,6 +109,11 @@ const IBonusTypeHandler * LibClasses::getBth() const
 	return bth;
 }
 
+const CIdentifierStorage * LibClasses::identifiers() const
+{
+	return &modh->getIdentifiers();
+}
+
 const spells::effects::Registry * LibClasses::spellEffects() const
 {
 	return spells::effects::GlobalRegistry::get();

+ 2 - 0
lib/VCMI_Lib.h

@@ -38,6 +38,7 @@ class CRmgTemplateStorage;
 class IHandlerBase;
 class IGameSettings;
 class GameSettings;
+class CIdentifierStorage;
 
 #if SCRIPTING_ENABLED
 namespace scripting
@@ -80,6 +81,7 @@ public:
 	spells::effects::Registry * spellEffects() override;
 
 	const IBonusTypeHandler * getBth() const; //deprecated
+	const CIdentifierStorage * identifiers() const;
 
 	CArtHandler * arth;
 	CHeroHandler * heroh;

+ 0 - 1
lib/battle/CBattleInfoCallback.cpp

@@ -24,7 +24,6 @@
 #include "../spells/CSpellHandler.h"
 #include "../mapObjects/CGTownInstance.h"
 #include "../BattleFieldHandler.h"
-#include "../CModHandler.h"
 #include "../Rect.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 4 - 4
lib/bonuses/Bonus.cpp

@@ -24,10 +24,10 @@
 #include "../CGeneralTextHandler.h"
 #include "../CSkillHandler.h"
 #include "../CArtHandler.h"
-#include "../CModHandler.h"
 #include "../TerrainHandler.h"
 #include "../StringConstants.h"
 #include "../battle/BattleInfo.h"
+#include "../modding/ModUtility.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -146,10 +146,10 @@ static JsonNode subtypeToJson(BonusType type, int subtype)
 	case BonusType::SPECIAL_PECULIAR_ENCHANT:
 	case BonusType::SPECIAL_ADD_VALUE_ENCHANT:
 	case BonusType::SPECIAL_FIXED_VALUE_ENCHANT:
-		return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "spell", SpellID::encode(subtype)));
+		return JsonUtils::stringNode(ModUtility::makeFullIdentifier("", "spell", SpellID::encode(subtype)));
 	case BonusType::IMPROVED_NECROMANCY:
 	case BonusType::SPECIAL_UPGRADE:
-		return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(subtype)));
+		return JsonUtils::stringNode(ModUtility::makeFullIdentifier("", "creature", CreatureID::encode(subtype)));
 	case BonusType::GENERATE_RESOURCE:
 		return JsonUtils::stringNode("resource." + GameConstants::RESOURCE_NAMES[subtype]);
 	default:
@@ -162,7 +162,7 @@ static JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo)
 	switch(type)
 	{
 	case BonusType::SPECIAL_UPGRADE:
-		return JsonUtils::stringNode(CModHandler::makeFullIdentifier("", "creature", CreatureID::encode(addInfo[0])));
+		return JsonUtils::stringNode(ModUtility::makeFullIdentifier("", "creature", CreatureID::encode(addInfo[0])));
 	default:
 		return addInfo.toJsonNode();
 	}

+ 0 - 1
lib/bonuses/Limiters.cpp

@@ -21,7 +21,6 @@
 #include "../CSkillHandler.h"
 #include "../CStack.h"
 #include "../CArtHandler.h"
-#include "../CModHandler.h"
 #include "../TerrainHandler.h"
 #include "../StringConstants.h"
 #include "../battle/BattleInfo.h"

+ 9 - 7
lib/campaign/CampaignHandler.cpp

@@ -16,14 +16,16 @@
 #include "../filesystem/CCompressedStream.h"
 #include "../filesystem/CMemoryStream.h"
 #include "../filesystem/CBinaryReader.h"
+#include "../modding/IdentifierStorage.h"
 #include "../VCMI_Lib.h"
 #include "../CGeneralTextHandler.h"
 #include "../TextOperations.h"
-#include "../CModHandler.h"
 #include "../Languages.h"
 #include "../StringConstants.h"
 #include "../mapping/CMapHeader.h"
 #include "../mapping/CMapService.h"
+#include "../modding/CModHandler.h"
+#include "../modding/ModScope.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -249,14 +251,14 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
 	
 	for(auto & k : reader["keepCreatures"].Vector())
 	{
-		if(auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "creature", k.String()))
+		if(auto identifier = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "creature", k.String()))
 			ret.monstersKeptByHero.insert(CreatureID(identifier.value()));
 		else
 			logGlobal->warn("VCMP Loading: keepCreatures contains unresolved identifier %s", k.String());
 	}
 	for(auto & k : reader["keepArtifacts"].Vector())
 	{
-		if(auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "artifact", k.String()))
+		if(auto identifier = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "artifact", k.String()))
 			ret.artifactsKeptByHero.insert(ArtifactID(identifier.value()));
 		else
 			logGlobal->warn("VCMP Loading: keepArtifacts contains unresolved identifier %s", k.String());
@@ -292,7 +294,7 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
 						if(int heroId = heroSpecialMap[bjson["hero"].String()])
 							bonus.info1 = heroId;
 						else
-							if(auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "hero", bjson["hero"].String()))
+							if(auto identifier = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", bjson["hero"].String()))
 								bonus.info1 = identifier.value();
 							else
 								logGlobal->warn("VCMP Loading: unresolved hero identifier %s", bjson["hero"].String());
@@ -305,14 +307,14 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
 							case CampaignBonusType::MONSTER:
 							case CampaignBonusType::SECONDARY_SKILL:
 							case CampaignBonusType::ARTIFACT:
-								if(auto identifier  = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), bjson["what"].String(), bjson["type"].String()))
+								if(auto identifier  = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), bjson["what"].String(), bjson["type"].String()))
 									bonus.info2 = identifier.value();
 								else
 									logGlobal->warn("VCMP Loading: unresolved %s identifier %s", bjson["what"].String(), bjson["type"].String());
 								break;
 								
 							case CampaignBonusType::SPELL_SCROLL:
-								if(auto Identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "spell", bjson["type"].String()))
+								if(auto Identifier = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "spell", bjson["type"].String()))
 									bonus.info2 = Identifier.value();
 								else
 									logGlobal->warn("VCMP Loading: unresolved spell scroll identifier %s", bjson["type"].String());
@@ -355,7 +357,7 @@ CampaignTravel CampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
 				if(int heroId = heroSpecialMap[bjson["hero"].String()])
 					bonus.info2 = heroId;
 				else
-					if (auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "hero", bjson["hero"].String()))
+					if (auto identifier = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", bjson["hero"].String()))
 						bonus.info2 = identifier.value();
 					else
 						logGlobal->warn("VCMP Loading: unresolved hero identifier %s", bjson["hero"].String());

+ 2 - 2
lib/filesystem/Filesystem.cpp

@@ -20,7 +20,7 @@
 #include "../GameConstants.h"
 #include "../VCMIDirs.h"
 #include "../CStopWatch.h"
-#include "../CModHandler.h"
+#include "../modding/ModScope.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -214,7 +214,7 @@ void CResourceHandler::load(const std::string &fsConfigURI, bool extractArchives
 
 	const JsonNode fsConfig(reinterpret_cast<char *>(fsConfigData.first.get()), fsConfigData.second);
 
-	addFilesystem("data", CModHandler::scopeBuiltin(), createFileSystem("", fsConfig["filesystem"], extractArchives));
+	addFilesystem("data", ModScope::scopeBuiltin(), createFileSystem("", fsConfig["filesystem"], extractArchives));
 }
 
 void CResourceHandler::addFilesystem(const std::string & parent, const std::string & identifier, ISimpleResourceLoader * loader)

+ 2 - 1
lib/gameState/CGameState.cpp

@@ -38,6 +38,7 @@
 #include "../mapping/CMap.h"
 #include "../mapping/CMapEditManager.h"
 #include "../mapping/CMapService.h"
+#include "../modding/IdentifierStorage.h"
 #include "../pathfinder/CPathfinder.h"
 #include "../pathfinder/PathfinderOptions.h"
 #include "../registerTypes/RegisterTypes.h"
@@ -1244,7 +1245,7 @@ BattleField CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & r
 	}
 
 	if(map->isCoastalTile(tile)) //coastal tile is always ground
-		return BattleField(*VLC->modh->identifiers.getIdentifier("core", "battlefield.sand_shore"));
+		return BattleField(*VLC->identifiers()->getIdentifier("core", "battlefield.sand_shore"));
 	
 	return BattleField(*RandomGeneratorUtil::nextItem(t.terType->battleFields, rand));
 }

+ 2 - 2
lib/mapObjectConstructors/AObjectTypeHandler.cpp

@@ -13,7 +13,7 @@
 
 #include "IObjectInfo.h"
 #include "../CGeneralTextHandler.h"
-#include "../CModHandler.h"
+#include "../modding/IdentifierStorage.h"
 #include "../VCMI_Lib.h"
 #include "../mapObjects/CGObjectInstance.h"
 #include "../mapObjects/ObjectTemplate.h"
@@ -100,7 +100,7 @@ void AObjectTypeHandler::init(const JsonNode & input)
 
 	if(!input["battleground"].isNull())
 	{
-		VLC->modh->identifiers.requestIdentifier("battlefield", input["battleground"], [this](int32_t identifier)
+		VLC->identifiers()->requestIdentifier("battlefield", input["battleground"], [this](int32_t identifier)
 		{
 			battlefield = BattleField(identifier);
 		});

+ 6 - 6
lib/mapObjectConstructors/CObjectClassesHandler.cpp

@@ -16,7 +16,6 @@
 #include "../GameConstants.h"
 #include "../StringConstants.h"
 #include "../CGeneralTextHandler.h"
-#include "../CModHandler.h"
 #include "../GameSettings.h"
 #include "../JsonNode.h"
 #include "../CSoundBase.h"
@@ -36,7 +35,8 @@
 #include "../mapObjects/MiscObjects.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../mapObjects/CGTownInstance.h"
-
+#include "../modding/IdentifierStorage.h"
+#include "../modding/CModHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -277,7 +277,7 @@ void CObjectClassesHandler::loadObject(std::string scope, std::string name, cons
 {
 	auto * object = loadFromJson(scope, data, name, objects.size());
 	objects.push_back(object);
-	VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
+	VLC->modh->getIdentifiers().registerObject(scope, "object", name, object->id);
 }
 
 void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
@@ -285,7 +285,7 @@ void CObjectClassesHandler::loadObject(std::string scope, std::string name, cons
 	auto * object = loadFromJson(scope, data, name, index);
 	assert(objects[(si32)index] == nullptr); // ensure that this id was not loaded before
 	objects[static_cast<si32>(index)] = object;
-	VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
+	VLC->modh->getIdentifiers().registerObject(scope, "object", name, object->id);
 }
 
 void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, si32 ID, si32 subID)
@@ -325,11 +325,11 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype)
 
 TObjectTypeHandler CObjectClassesHandler::getHandlerFor(const std::string & scope, const std::string & type, const std::string & subtype) const
 {
-	std::optional<si32> id = VLC->modh->identifiers.getIdentifier(scope, "object", type);
+	std::optional<si32> id = VLC->identifiers()->getIdentifier(scope, "object", type);
 	if(id)
 	{
 		auto * object = objects[id.value()];
-		std::optional<si32> subID = VLC->modh->identifiers.getIdentifier(scope, object->getJsonKey(), subtype);
+		std::optional<si32> subID = VLC->identifiers()->getIdentifier(scope, object->getJsonKey(), subtype);
 
 		if (subID)
 			return object->objects[subID.value()];

+ 6 - 5
lib/mapObjectConstructors/CommonConstructors.cpp

@@ -12,7 +12,6 @@
 
 #include "../CGeneralTextHandler.h"
 #include "../CHeroHandler.h"
-#include "../CModHandler.h"
 #include "../CTownHandler.h"
 #include "../IGameCallback.h"
 #include "../JsonRandom.h"
@@ -26,6 +25,8 @@
 #include "../mapObjects/MiscObjects.h"
 #include "../mapObjects/ObjectTemplate.h"
 
+#include "../modding/IdentifierStorage.h"
+
 #include "../mapping/CMapDefines.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -57,7 +58,7 @@ std::string ResourceInstanceConstructor::getNameTextID() const
 
 void CTownInstanceConstructor::initTypeData(const JsonNode & input)
 {
-	VLC->modh->identifiers.requestIdentifier("faction", input["faction"], [&](si32 index)
+	VLC->identifiers()->requestIdentifier("faction", input["faction"], [&](si32 index)
 	{
 		faction = (*VLC->townh)[index];
 	});
@@ -76,7 +77,7 @@ void CTownInstanceConstructor::afterLoadFinalization()
 	{
 		filters[entry.first] = LogicalExpression<BuildingID>(entry.second, [this](const JsonNode & node)
 		{
-			return BuildingID(VLC->modh->identifiers.getIdentifier("building." + faction->getJsonKey(), node.Vector()[0]).value());
+			return BuildingID(VLC->identifiers()->getIdentifier("building." + faction->getJsonKey(), node.Vector()[0]).value());
 		});
 	}
 }
@@ -118,7 +119,7 @@ std::string CTownInstanceConstructor::getNameTextID() const
 
 void CHeroInstanceConstructor::initTypeData(const JsonNode & input)
 {
-	VLC->modh->identifiers.requestIdentifier(
+	VLC->identifiers()->requestIdentifier(
 		"heroClass",
 		input["heroClass"],
 		[&](si32 index) { heroClass = VLC->heroh->classes[index]; });
@@ -132,7 +133,7 @@ void CHeroInstanceConstructor::afterLoadFinalization()
 	{
 		filters[entry.first] = LogicalExpression<HeroTypeID>(entry.second, [](const JsonNode & node)
 		{
-			return HeroTypeID(VLC->modh->identifiers.getIdentifier("hero", node.Vector()[0]).value());
+			return HeroTypeID(VLC->identifiers()->getIdentifier("hero", node.Vector()[0]).value());
 		});
 	}
 }

+ 2 - 2
lib/mapObjectConstructors/DwellingInstanceConstructor.cpp

@@ -12,10 +12,10 @@
 
 #include "../CCreatureHandler.h"
 #include "../CGeneralTextHandler.h"
-#include "../CModHandler.h"
 #include "../JsonRandom.h"
 #include "../VCMI_Lib.h"
 #include "../mapObjects/CGDwelling.h"
+#include "../modding/IdentifierStorage.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -43,7 +43,7 @@ void DwellingInstanceConstructor::initTypeData(const JsonNode & input)
 
 		for(auto currentCreature = 0; currentCreature < creaturesNumber; currentCreature++)
 		{
-			VLC->modh->identifiers.requestIdentifier("creature", creaturesOnLevel[currentCreature], [=] (si32 index)
+			VLC->identifiers()->requestIdentifier("creature", creaturesOnLevel[currentCreature], [=] (si32 index)
 			{
 				availableCreatures[currentLevel][currentCreature] = VLC->creh->objects[index];
 			});

+ 2 - 2
lib/mapObjectConstructors/ShipyardInstanceConstructor.cpp

@@ -11,7 +11,7 @@
 #include "ShipyardInstanceConstructor.h"
 
 #include "../mapObjects/MiscObjects.h"
-#include "../CModHandler.h"
+#include "../modding/IdentifierStorage.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -22,7 +22,7 @@ void ShipyardInstanceConstructor::initTypeData(const JsonNode & config)
 
 void ShipyardInstanceConstructor::initializeObject(CGShipyard * shipyard) const
 {
-	shipyard->createdBoat = BoatId(*VLC->modh->identifiers.getIdentifier("core:boat", parameters["boat"]));
+	shipyard->createdBoat = BoatId(*VLC->identifiers()->getIdentifier("core:boat", parameters["boat"]));
 }
 
 VCMI_LIB_NAMESPACE_END

+ 2 - 2
lib/mapObjects/CGHeroInstance.cpp

@@ -21,7 +21,6 @@
 #include "../TerrainHandler.h"
 #include "../RoadHandler.h"
 #include "../GameSettings.h"
-#include "../CModHandler.h"
 #include "../CSoundBase.h"
 #include "../spells/CSpellHandler.h"
 #include "../CSkillHandler.h"
@@ -35,6 +34,7 @@
 #include "../serializer/JsonSerializeFormat.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
+#include "../modding/ModScope.h"
 #include "../StringConstants.h"
 #include "../battle/Unit.h"
 
@@ -1487,7 +1487,7 @@ void CGHeroInstance::setHeroTypeName(const std::string & identifier)
 {
 	if(ID == Obj::HERO || ID == Obj::PRISON)
 	{
-		auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "hero", identifier);
+		auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", identifier);
 
 		if(rawId)
 			subID = rawId.value();

+ 2 - 2
lib/mapObjects/CGTownInstance.cpp

@@ -17,7 +17,6 @@
 #include "../NetPacks.h"
 #include "../CConfigHandler.h"
 #include "../CGeneralTextHandler.h"
-#include "../CModHandler.h"
 #include "../IGameCallback.h"
 #include "../gameState/CGameState.h"
 #include "../mapping/CMap.h"
@@ -25,6 +24,7 @@
 #include "../TerrainHandler.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
+#include "../modding/ModScope.h"
 #include "../serializer/JsonSerializeFormat.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -1097,7 +1097,7 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 	{
 		auto decodeBuilding = [this](const std::string & identifier) -> si32
 		{
-			auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), getTown()->getBuildingScope(), identifier);
+			auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), getTown()->getBuildingScope(), identifier);
 
 			if(rawId)
 				return rawId.value();

+ 5 - 4
lib/mapObjects/CQuest.cpp

@@ -22,11 +22,12 @@
 #include "../IGameCallback.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../serializer/JsonSerializeFormat.h"
-#include "../CModHandler.h"
 #include "../GameConstants.h"
 #include "../StringConstants.h"
 #include "../CSkillHandler.h"
 #include "../mapping/CMap.h"
+#include "../modding/ModScope.h"
+#include "../modding/ModUtility.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -1003,7 +1004,7 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler)
 		}
 		if(rewardType != NOTHING)
 		{
-			fullIdentifier = CModHandler::makeFullIdentifier(scope, metaTypeName, identifier);
+			fullIdentifier = ModUtility::makeFullIdentifier(scope, metaTypeName, identifier);
 			handler.serializeInt(fullIdentifier, amount);
 		}
 	}
@@ -1023,7 +1024,7 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler)
 			fullIdentifier = iter->first;
 		}
 
-		CModHandler::parseIdentifier(fullIdentifier, scope, metaTypeName, identifier);
+		ModUtility::parseIdentifier(fullIdentifier, scope, metaTypeName, identifier);
 
 		auto it = REWARD_RMAP.find(metaTypeName);
 
@@ -1065,7 +1066,7 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler)
 
 		if(doRequest)
 		{
-			auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), fullIdentifier, false);
+			auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), fullIdentifier, false);
 
 			if(rawId)
 			{

+ 4 - 4
lib/mapObjects/MiscObjects.cpp

@@ -15,7 +15,6 @@
 #include "../NetPacks.h"
 #include "../CGeneralTextHandler.h"
 #include "../CSoundBase.h"
-#include "../CModHandler.h"
 #include "../CSkillHandler.h"
 #include "../spells/CSpellHandler.h"
 #include "../IGameCallback.h"
@@ -25,6 +24,7 @@
 #include "../serializer/JsonSerializeFormat.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
+#include "../modding/ModScope.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -1165,7 +1165,7 @@ void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
 		bonusType = RANDOM;
 		if(!json["rewardPrimSkill"].String().empty())
 		{
-			auto raw = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "primSkill", json["rewardPrimSkill"].String());
+			auto raw = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), "primSkill", json["rewardPrimSkill"].String());
 			if(raw)
 			{
 				bonusType = PRIM_SKILL;
@@ -1174,7 +1174,7 @@ void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
 		}
 		else if(!json["rewardSkill"].String().empty())
 		{
-			auto raw = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "skill", json["rewardSkill"].String());
+			auto raw = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), "skill", json["rewardSkill"].String());
 			if(raw)
 			{
 				bonusType = SECONDARY_SKILL;
@@ -1183,7 +1183,7 @@ void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
 		}
 		else if(!json["rewardSpell"].String().empty())
 		{
-			auto raw = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "spell", json["rewardSpell"].String());
+			auto raw = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), "spell", json["rewardSpell"].String());
 			if(raw)
 			{
 				bonusType = SPELL;

+ 2 - 2
lib/mapObjects/ObjectTemplate.cpp

@@ -16,11 +16,11 @@
 #include "../GameConstants.h"
 #include "../StringConstants.h"
 #include "../CGeneralTextHandler.h"
-#include "../CModHandler.h"
 #include "../JsonNode.h"
 #include "../TerrainHandler.h"
 
 #include "../mapObjectConstructors/CRewardableConstructor.h"
+#include "../modding/IdentifierStorage.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -273,7 +273,7 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
 	{
 		for(const auto & entry : node["allowedTerrains"].Vector())
 		{
-			VLC->modh->identifiers.requestIdentifier("terrain", entry, [this](int32_t identifier){
+			VLC->identifiers()->requestIdentifier("terrain", entry, [this](int32_t identifier){
 				allowedTerrains.insert(TerrainId(identifier));
 			});
 		}

+ 1 - 1
lib/mapping/CMapHeader.h

@@ -10,7 +10,7 @@
 
 #pragma once
 
-#include "../CModVersion.h"
+#include "../modding/CModVersion.h"
 #include "../LogicalExpression.h"
 #include "../int3.h"
 #include "../MetaString.h"

+ 0 - 1
lib/mapping/CMapInfo.cpp

@@ -25,7 +25,6 @@
 #include "../CCreatureHandler.h"
 #include "../GameSettings.h"
 #include "../CHeroHandler.h"
-#include "../CModHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 4 - 2
lib/mapping/CMapService.cpp

@@ -15,7 +15,9 @@
 #include "../filesystem/CCompressedStream.h"
 #include "../filesystem/CMemoryStream.h"
 #include "../filesystem/CMemoryBuffer.h"
-#include "../CModHandler.h"
+#include "../modding/CModHandler.h"
+#include "../modding/ModScope.h"
+#include "../modding/CModInfo.h"
 #include "../Languages.h"
 #include "../VCMI_Lib.h"
 
@@ -158,7 +160,7 @@ static JsonNode loadPatches(std::string path)
 	for (auto & entry : node.Struct())
 		JsonUtils::validate(entry.second, "vcmi:mapHeader", "patch for " + entry.first);
 
-	node.setMeta(CModHandler::scopeMap());
+	node.setMeta(ModScope::scopeMap());
 	return node;
 }
 

+ 0 - 1
lib/mapping/MapFormatH3M.cpp

@@ -21,7 +21,6 @@
 #include "../CHeroHandler.h"
 #include "../CSkillHandler.h"
 #include "../CStopWatch.h"
-#include "../CModHandler.h"
 #include "../GameSettings.h"
 #include "../RiverHandler.h"
 #include "../RoadHandler.h"

+ 8 - 7
lib/mapping/MapFormatJson.cpp

@@ -17,7 +17,6 @@
 #include "CMap.h"
 #include "MapFormat.h"
 #include "../ArtifactUtils.h"
-#include "../CModHandler.h"
 #include "../CHeroHandler.h"
 #include "../CTownHandler.h"
 #include "../VCMI_Lib.h"
@@ -29,6 +28,8 @@
 #include "../mapObjects/ObjectTemplate.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../mapObjects/CGTownInstance.h"
+#include "../modding/ModScope.h"
+#include "../modding/ModUtility.h"
 #include "../spells/CSpellHandler.h"
 #include "../CSkillHandler.h"
 #include "../StringConstants.h"
@@ -191,7 +192,7 @@ namespace TriggeredEventsDetail
 			break;
 		}
 
-		return CModHandler::makeFullIdentifier("", metaclassName, identifier);
+		return ModUtility::makeFullIdentifier("", metaclassName, identifier);
 	}
 
 	static EventCondition JsonToCondition(const JsonNode & node)
@@ -219,11 +220,11 @@ namespace TriggeredEventsDetail
 					std::string metaTypeName;
 					std::string scope;
 					std::string identifier;
-					CModHandler::parseIdentifier(fullIdentifier, scope, metaTypeName, identifier);
+					ModUtility::parseIdentifier(fullIdentifier, scope, metaTypeName, identifier);
 
 					event.metaType = decodeMetaclass(metaTypeName);
 
-					auto type = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), fullIdentifier, false);
+					auto type = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), fullIdentifier, false);
 
 					if(type)
 						event.objectType = type.value();
@@ -242,7 +243,7 @@ namespace TriggeredEventsDetail
 					//old format
 					if (data["type"].getType() == JsonNode::JsonType::DATA_STRING)
 					{
-						auto identifier = VLC->modh->identifiers.getIdentifier(data["type"]);
+						auto identifier = VLC->identifiers()->getIdentifier(data["type"]);
 						if(identifier)
 							event.objectType = identifier.value();
 						else
@@ -1157,7 +1158,7 @@ void CMapLoaderJson::MapObjectLoader::construct()
 		return;
 	}
 
-	auto handler = VLC->objtypeh->getHandlerFor( CModHandler::scopeMap(), typeName, subtypeName);
+	auto handler = VLC->objtypeh->getHandlerFor( ModScope::scopeMap(), typeName, subtypeName);
 
 	auto * appearance = new ObjectTemplate;
 
@@ -1194,7 +1195,7 @@ void CMapLoaderJson::MapObjectLoader::configure()
 		if(art->ID == Obj::SPELL_SCROLL)
 		{
 			auto spellIdentifier = configuration["options"]["spell"].String();
-			auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "spell", spellIdentifier);
+			auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), "spell", spellIdentifier);
 			if(rawId)
 				spellID = rawId.value();
 			else

+ 5 - 5
lib/mapping/MapIdentifiersH3M.cpp

@@ -13,13 +13,13 @@
 
 #include "../JsonNode.h"
 #include "../VCMI_Lib.h"
-#include "../CModHandler.h"
 #include "../CTownHandler.h"
 #include "../CHeroHandler.h"
 #include "../filesystem/Filesystem.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjects/ObjectTemplate.h"
+#include "../modding/IdentifierStorage.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -29,7 +29,7 @@ void MapIdentifiersH3M::loadMapping(std::map<IdentifierID, IdentifierID> & resul
 	for (auto entry : mapping.Struct())
 	{
 		IdentifierID sourceID (entry.second.Integer());
-		IdentifierID targetID (*VLC->modh->identifiers.getIdentifier(entry.second.meta, identifierName, entry.first));
+		IdentifierID targetID (*VLC->identifiers()->getIdentifier(entry.second.meta, identifierName, entry.first));
 
 		result[sourceID] = targetID;
 	}
@@ -39,13 +39,13 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping)
 {
 	for (auto entryFaction : mapping["buildings"].Struct())
 	{
-		FactionID factionID (*VLC->modh->identifiers.getIdentifier(entryFaction.second.meta, "faction", entryFaction.first));
+		FactionID factionID (*VLC->identifiers()->getIdentifier(entryFaction.second.meta, "faction", entryFaction.first));
 		auto buildingMap = entryFaction.second;
 
 		for (auto entryBuilding : buildingMap.Struct())
 		{
 			BuildingID sourceID (entryBuilding.second.Integer());
-			BuildingID targetID (*VLC->modh->identifiers.getIdentifier(entryBuilding.second.meta, "building." + VLC->factions()->getById(factionID)->getJsonKey(), entryBuilding.first));
+			BuildingID targetID (*VLC->identifiers()->getIdentifier(entryBuilding.second.meta, "building." + VLC->factions()->getById(factionID)->getJsonKey(), entryBuilding.first));
 
 			mappingFactionBuilding[factionID][sourceID] = targetID;
 		}
@@ -90,7 +90,7 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping)
 	for (auto entry : mapping["portraits"].Struct())
 	{
 		int32_t sourceID = entry.second.Integer();
-		int32_t targetID = *VLC->modh->identifiers.getIdentifier(entry.second.meta, "hero", entry.first);
+		int32_t targetID = *VLC->identifiers()->getIdentifier(entry.second.meta, "hero", entry.first);
 		int32_t iconID = VLC->heroTypes()->getByIndex(targetID)->getIconIndex();
 
 		mappingHeroPortrait[sourceID] = iconID;

+ 59 - 769
lib/modding/CModHandler.cpp

@@ -9,559 +9,24 @@
  */
 #include "StdInc.h"
 #include "CModHandler.h"
-#include "rmg/CRmgTemplateStorage.h"
-#include "filesystem/AdapterLoaders.h"
-#include "filesystem/CFilesystemLoader.h"
-#include "filesystem/Filesystem.h"
-
-#include "CCreatureHandler.h"
-#include "CArtHandler.h"
-#include "CTownHandler.h"
-#include "CHeroHandler.h"
-#include "StringConstants.h"
-#include "CStopWatch.h"
-#include "IHandlerBase.h"
-#include "spells/CSpellHandler.h"
-#include "CSkillHandler.h"
-#include "CGeneralTextHandler.h"
-#include "Languages.h"
-#include "ScriptHandler.h"
-#include "RoadHandler.h"
-#include "GameSettings.h"
-#include "RiverHandler.h"
-#include "TerrainHandler.h"
-#include "BattleFieldHandler.h"
-#include "ObstacleHandler.h"
-#include "mapObjectConstructors/CObjectClassesHandler.h"
-
-#include <vstd/StringUtils.h>
 
-VCMI_LIB_NAMESPACE_BEGIN
-
-CIdentifierStorage::CIdentifierStorage():
-	state(LOADING)
-{
-}
-
-void CIdentifierStorage::checkIdentifier(std::string & ID)
-{
-	if (boost::algorithm::ends_with(ID, "."))
-		logMod->warn("BIG WARNING: identifier %s seems to be broken!", ID);
-	else
-	{
-		size_t pos = 0;
-		do
-		{
-			if (std::tolower(ID[pos]) != ID[pos] ) //Not in camelCase
-			{
-				logMod->warn("Warning: identifier %s is not in camelCase!", ID);
-				ID[pos] = std::tolower(ID[pos]);// Try to fix the ID
-			}
-			pos = ID.find('.', pos);
-		}
-		while(pos++ != std::string::npos);
-	}
-}
-
-void CIdentifierStorage::requestIdentifier(ObjectCallback callback)
-{
-	checkIdentifier(callback.type);
-	checkIdentifier(callback.name);
-
-	assert(!callback.localScope.empty());
-
-	if (state != FINISHED) // enqueue request if loading is still in progress
-		scheduledRequests.push_back(callback);
-	else // execute immediately for "late" requests
-		resolveIdentifier(callback);
-}
-
-CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameWithType(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback, bool optional)
-{
-	assert(!scope.empty());
-
-	auto scopeAndFullName = vstd::splitStringToPair(fullName, ':');
-	auto typeAndName = vstd::splitStringToPair(scopeAndFullName.second, '.');
-
-	if (scope == scopeAndFullName.first)
-		logMod->debug("Target scope for identifier '%s' is redundant! Identifier already defined in mod '%s'", fullName, scope);
-
-	ObjectCallback result;
-	result.localScope = scope;
-	result.remoteScope = scopeAndFullName.first;
-	result.type = typeAndName.first;
-	result.name = typeAndName.second;
-	result.callback = callback;
-	result.optional = optional;
-	return result;
-}
-
-CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional)
-{
-	assert(!scope.empty());
-
-	auto scopeAndFullName = vstd::splitStringToPair(fullName, ':');
-	auto typeAndName = vstd::splitStringToPair(scopeAndFullName.second, '.');
-
-	if(!typeAndName.first.empty())
-	{
-		if (typeAndName.first != type)
-			logMod->error("Identifier '%s' from mod '%s' requested with different type! Type '%s' expected!", fullName, scope, type);
-		else
-			logMod->debug("Target type for identifier '%s' defined in mod '%s' is redundant!", fullName, scope);
-	}
-
-	if (scope == scopeAndFullName.first)
-		logMod->debug("Target scope for identifier '%s' is redundant! Identifier already defined in mod '%s'", fullName, scope);
-
-	ObjectCallback result;
-	result.localScope = scope;
-	result.remoteScope = scopeAndFullName.first;
-	result.type = type;
-	result.name = typeAndName.second;
-	result.callback = callback;
-	result.optional = optional;
-	return result;
-}
-
-void CIdentifierStorage::requestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback)
-{
-	requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, false));
-}
-
-void CIdentifierStorage::requestIdentifier(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback)
-{
-	requestIdentifier(ObjectCallback::fromNameWithType(scope, fullName, callback, false));
-}
+#include "CModInfo.h"
+#include "ModScope.h"
+#include "ContentTypeHandler.h"
+#include "IdentifierStorage.h"
+#include "ModIncompatibility.h"
+
+#include "../CCreatureHandler.h"
+#include "../CGeneralTextHandler.h"
+#include "../CStopWatch.h"
+#include "../GameSettings.h"
+#include "../Languages.h"
+#include "../ScriptHandler.h"
+#include "../StringConstants.h"
+#include "../filesystem/Filesystem.h"
+#include "../spells/CSpellHandler.h"
 
-void CIdentifierStorage::requestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback)
-{
-	requestIdentifier(ObjectCallback::fromNameAndType(name.meta, type, name.String(), callback, false));
-}
-
-void CIdentifierStorage::requestIdentifier(const JsonNode & name, const std::function<void(si32)> & callback)
-{
-	requestIdentifier(ObjectCallback::fromNameWithType(name.meta, name.String(), callback, false));
-}
-
-void CIdentifierStorage::tryRequestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback)
-{
-	requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, true));
-}
-
-void CIdentifierStorage::tryRequestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback)
-{
-	requestIdentifier(ObjectCallback::fromNameAndType(name.meta, type, name.String(), callback, true));
-}
-
-std::optional<si32> CIdentifierStorage::getIdentifier(const std::string & scope, const std::string & type, const std::string & name, bool silent)
-{
-	auto idList = getPossibleIdentifiers(ObjectCallback::fromNameAndType(scope, type, name, std::function<void(si32)>(), silent));
-
-	if (idList.size() == 1)
-		return idList.front().id;
-	if (!silent)
-		logMod->error("Failed to resolve identifier %s of type %s from mod %s", name , type ,scope);
-
-	return std::optional<si32>();
-}
-
-std::optional<si32> CIdentifierStorage::getIdentifier(const std::string & type, const JsonNode & name, bool silent)
-{
-	auto idList = getPossibleIdentifiers(ObjectCallback::fromNameAndType(name.meta, type, name.String(), std::function<void(si32)>(), silent));
-
-	if (idList.size() == 1)
-		return idList.front().id;
-	if (!silent)
-		logMod->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), type, name.meta);
-
-	return std::optional<si32>();
-}
-
-std::optional<si32> CIdentifierStorage::getIdentifier(const JsonNode & name, bool silent)
-{
-	auto idList = getPossibleIdentifiers(ObjectCallback::fromNameWithType(name.meta, name.String(), std::function<void(si32)>(), silent));
-
-	if (idList.size() == 1)
-		return idList.front().id;
-	if (!silent)
-		logMod->error("Failed to resolve identifier %s from mod %s", name.String(), name.meta);
-
-	return std::optional<si32>();
-}
-
-std::optional<si32> CIdentifierStorage::getIdentifier(const std::string & scope, const std::string & fullName, bool silent)
-{
-	auto idList = getPossibleIdentifiers(ObjectCallback::fromNameWithType(scope, fullName, std::function<void(si32)>(), silent));
-
-	if (idList.size() == 1)
-		return idList.front().id;
-	if (!silent)
-		logMod->error("Failed to resolve identifier %s from mod %s", fullName, scope);
-
-	return std::optional<si32>();
-}
-
-void CIdentifierStorage::registerObject(const std::string & scope, const std::string & type, const std::string & name, si32 identifier)
-{
-	ObjectData data;
-	data.scope = scope;
-	data.id = identifier;
-
-	std::string fullID = type + '.' + name;
-	checkIdentifier(fullID);
-
-	std::pair<const std::string, ObjectData> mapping = std::make_pair(fullID, data);
-	if(!vstd::containsMapping(registeredObjects, mapping))
-	{
-		logMod->trace("registered %s as %s:%s", fullID, scope, identifier);
-		registeredObjects.insert(mapping);
-	}
-}
-
-std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdentifiers(const ObjectCallback & request)
-{
-	std::set<std::string> allowedScopes;
-	bool isValidScope = true;
-
-	// called have not specified destination mod explicitly
-	if (request.remoteScope.empty())
-	{
-		// special scope that should have access to all in-game objects
-		if (request.localScope == CModHandler::scopeGame())
-		{
-			for(const auto & modName : VLC->modh->getActiveMods())
-				allowedScopes.insert(modName);
-		}
-
-		// normally ID's from all required mods, own mod and virtual built-in mod are allowed
-		else if(request.localScope != CModHandler::scopeBuiltin() && !request.localScope.empty())
-		{
-			allowedScopes = VLC->modh->getModDependencies(request.localScope, isValidScope);
-
-			if(!isValidScope)
-				return std::vector<ObjectData>();
-
-			allowedScopes.insert(request.localScope);
-		}
-
-		// all mods can access built-in mod
-		allowedScopes.insert(CModHandler::scopeBuiltin());
-	}
-	else
-	{
-		//if destination mod was specified explicitly, restrict lookup to this mod
-		if(request.remoteScope == CModHandler::scopeBuiltin() )
-		{
-			//built-in mod is an implicit dependency for all mods, allow access into it
-			allowedScopes.insert(request.remoteScope);
-		}
-		else if ( request.localScope == CModHandler::scopeGame() )
-		{
-			// allow access, this is special scope that should have access to all in-game objects
-			allowedScopes.insert(request.remoteScope);
-		}
-		else if(request.remoteScope == request.localScope )
-		{
-			// allow self-access
-			allowedScopes.insert(request.remoteScope);
-		}
-		else
-		{
-			// allow access only if mod is in our dependencies
-			auto myDeps = VLC->modh->getModDependencies(request.localScope, isValidScope);
-
-			if(!isValidScope)
-				return std::vector<ObjectData>();
-
-			if(myDeps.count(request.remoteScope))
-				allowedScopes.insert(request.remoteScope);
-		}
-	}
-
-	std::string fullID = request.type + '.' + request.name;
-
-	auto entries = registeredObjects.equal_range(fullID);
-	if (entries.first != entries.second)
-	{
-		std::vector<ObjectData> locatedIDs;
-
-		for (auto it = entries.first; it != entries.second; it++)
-		{
-			if (vstd::contains(allowedScopes, it->second.scope))
-			{
-				locatedIDs.push_back(it->second);
-			}
-		}
-		return locatedIDs;
-	}
-	return std::vector<ObjectData>();
-}
-
-bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request)
-{
-	auto identifiers = getPossibleIdentifiers(request);
-	if (identifiers.size() == 1) // normally resolved ID
-	{
-		request.callback(identifiers.front().id);
-		return true;
-	}
-
-	if (request.optional && identifiers.empty()) // failed to resolve optinal ID
-	{
-		return true;
-	}
-
-	// error found. Try to generate some debug info
-	if(identifiers.empty())
-		logMod->error("Unknown identifier!");
-	else
-		logMod->error("Ambiguous identifier request!");
-
-	 logMod->error("Request for %s.%s from mod %s", request.type, request.name, request.localScope);
-
-	for(const auto & id : identifiers)
-	{
-		logMod->error("\tID is available in mod %s", id.scope);
-	}
-	return false;
-}
-
-void CIdentifierStorage::finalize()
-{
-	state = FINALIZING;
-	bool errorsFound = false;
-
-	while ( !scheduledRequests.empty() )
-	{
-		// 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)
-	{
-		for(const auto & object : registeredObjects)
-		{
-			logMod->trace("%s : %s -> %d", object.second.scope, object.first, object.second.id);
-		}
-		logMod->error("All known identifiers were dumped into log file");
-	}
-	assert(errorsFound == false);
-	state = FINISHED;
-}
-
-ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string & objectName):
-	handler(handler),
-	objectName(objectName),
-	originalData(handler->loadLegacyData())
-{
-	for(auto & node : originalData)
-	{
-		node.setMeta(CModHandler::scopeBuiltin());
-	}
-}
-
-bool ContentTypeHandler::preloadModData(const std::string & modName, const std::vector<std::string> & fileList, bool validate)
-{
-	bool result = false;
-	JsonNode data = JsonUtils::assembleFromFiles(fileList, result);
-	data.setMeta(modName);
-
-	ModInfo & modInfo = modData[modName];
-
-	for(auto entry : data.Struct())
-	{
-		size_t colon = entry.first.find(':');
-
-		if (colon == std::string::npos)
-		{
-			// normal object, local to this mod
-			modInfo.modData[entry.first].swap(entry.second);
-		}
-		else
-		{
-			std::string remoteName = entry.first.substr(0, colon);
-			std::string objectName = entry.first.substr(colon + 1);
-
-			// patching this mod? Send warning and continue - this situation can be handled normally
-			if (remoteName == modName)
-				logMod->warn("Redundant namespace definition for %s", objectName);
-
-			logMod->trace("Patching object %s (%s) from %s", objectName, remoteName, modName);
-			JsonNode & remoteConf = modData[remoteName].patches[objectName];
-
-			JsonUtils::merge(remoteConf, entry.second);
-		}
-	}
-	return result;
-}
-
-bool ContentTypeHandler::loadMod(const std::string & modName, bool validate)
-{
-	ModInfo & modInfo = modData[modName];
-	bool result = true;
-
-	auto performValidate = [&,this](JsonNode & data, const std::string & name){
-		handler->beforeValidate(data);
-		if (validate)
-			result &= JsonUtils::validate(data, "vcmi:" + objectName, name);
-	};
-
-	// apply patches
-	if (!modInfo.patches.isNull())
-		JsonUtils::merge(modInfo.modData, modInfo.patches);
-
-	for(auto & entry : modInfo.modData.Struct())
-	{
-		const std::string & name = entry.first;
-		JsonNode & data = entry.second;
-
-		if (data.meta != modName)
-			logMod->warn("Mod %s is attempting to inject object %s into mod %s! This may not be supported in future versions!", data.meta, name, modName);
-
-		if (vstd::contains(data.Struct(), "index") && !data["index"].isNull())
-		{
-			if (modName != "core")
-				logMod->warn("Mod %s is attempting to load original data! This should be reserved for built-in mod.", modName);
-
-			// try to add H3 object data
-			size_t index = static_cast<size_t>(data["index"].Float());
-
-			if(originalData.size() > index)
-			{
-				logMod->trace("found original data in loadMod(%s) at index %d", name, index);
-				JsonUtils::merge(originalData[index], data);
-				std::swap(originalData[index], data);
-				originalData[index].clear(); // do not use same data twice (same ID)
-			}
-			else
-			{
-				logMod->trace("no original data in loadMod(%s) at index %d", name, index);
-			}
-			performValidate(data, name);
-			handler->loadObject(modName, name, data, index);
-		}
-		else
-		{
-			// normal new object
-			logMod->trace("no index in loadMod(%s)", name);
-			performValidate(data,name);
-			handler->loadObject(modName, name, data);
-		}
-	}
-	return result;
-}
-
-void ContentTypeHandler::loadCustom()
-{
-	handler->loadCustom();
-}
-
-void ContentTypeHandler::afterLoadFinalization()
-{
-	handler->afterLoadFinalization();
-}
-
-void CContentHandler::init()
-{
-	handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass")));
-	handlers.insert(std::make_pair("artifacts", ContentTypeHandler(VLC->arth, "artifact")));
-	handlers.insert(std::make_pair("creatures", ContentTypeHandler(VLC->creh, "creature")));
-	handlers.insert(std::make_pair("factions", ContentTypeHandler(VLC->townh, "faction")));
-	handlers.insert(std::make_pair("objects", ContentTypeHandler(VLC->objtypeh, "object")));
-	handlers.insert(std::make_pair("heroes", ContentTypeHandler(VLC->heroh, "hero")));
-	handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
-	handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill")));
-	handlers.insert(std::make_pair("templates", ContentTypeHandler(VLC->tplh, "template")));
-#if SCRIPTING_ENABLED
-	handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script")));
-#endif
-	handlers.insert(std::make_pair("battlefields", ContentTypeHandler(VLC->battlefieldsHandler, "battlefield")));
-	handlers.insert(std::make_pair("terrains", ContentTypeHandler(VLC->terrainTypeHandler, "terrain")));
-	handlers.insert(std::make_pair("rivers", ContentTypeHandler(VLC->riverTypeHandler, "river")));
-	handlers.insert(std::make_pair("roads", ContentTypeHandler(VLC->roadTypeHandler, "road")));
-	handlers.insert(std::make_pair("obstacles", ContentTypeHandler(VLC->obstacleHandler, "obstacle")));
-	//TODO: any other types of moddables?
-}
-
-bool CContentHandler::preloadModData(const std::string & modName, JsonNode modConfig, bool validate)
-{
-	bool result = true;
-	for(auto & handler : handlers)
-	{
-		result &= handler.second.preloadModData(modName, modConfig[handler.first].convertTo<std::vector<std::string> >(), validate);
-	}
-	return result;
-}
-
-bool CContentHandler::loadMod(const std::string & modName, bool validate)
-{
-	bool result = true;
-	for(auto & handler : handlers)
-	{
-		result &= handler.second.loadMod(modName, validate);
-	}
-	return result;
-}
-
-void CContentHandler::loadCustom()
-{
-	for(auto & handler : handlers)
-	{
-		handler.second.loadCustom();
-	}
-}
-
-void CContentHandler::afterLoadFinalization()
-{
-	for(auto & handler : handlers)
-	{
-		handler.second.afterLoadFinalization();
-	}
-}
-
-void CContentHandler::preloadData(CModInfo & mod)
-{
-	bool validate = (mod.validation != CModInfo::PASSED);
-
-	// print message in format [<8-symbols checksum>] <modname>
-	logMod->info("\t\t[%08x]%s", mod.checksum, mod.name);
-
-	if (validate && mod.identifier != CModHandler::scopeBuiltin())
-	{
-		if (!JsonUtils::validate(mod.config, "vcmi:mod", mod.identifier))
-			mod.validation = CModInfo::FAILED;
-	}
-	if (!preloadModData(mod.identifier, mod.config, validate))
-		mod.validation = CModInfo::FAILED;
-}
-
-void CContentHandler::load(CModInfo & mod)
-{
-	bool validate = (mod.validation != CModInfo::PASSED);
-
-	if (!loadMod(mod.identifier, validate))
-		mod.validation = CModInfo::FAILED;
-
-	if (validate)
-	{
-		if (mod.validation != CModInfo::FAILED)
-			logMod->info("\t\t[DONE] %s", mod.name);
-		else
-			logMod->error("\t\t[FAIL] %s", mod.name);
-	}
-	else
-		logMod->info("\t\t[SKIP] %s", mod.name);
-}
-
-const ContentTypeHandler & CContentHandler::operator[](const std::string & name) const
-{
-	return handlers.at(name);
-}
+VCMI_LIB_NAMESPACE_BEGIN
 
 static JsonNode loadModSettings(const std::string & path)
 {
@@ -574,149 +39,31 @@ static JsonNode loadModSettings(const std::string & path)
 	return JsonNode();
 }
 
-JsonNode addMeta(JsonNode config, const std::string & meta)
-{
-	config.setMeta(meta);
-	return config;
-}
-
-CModInfo::CModInfo():
-	checksum(0),
-	explicitlyEnabled(false),
-	implicitlyEnabled(true),
-	validation(PENDING)
-{
-
-}
-
-CModInfo::CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config):
-	identifier(identifier),
-	name(config["name"].String()),
-	description(config["description"].String()),
-	dependencies(config["depends"].convertTo<std::set<std::string>>()),
-	conflicts(config["conflicts"].convertTo<std::set<std::string>>()),
-	checksum(0),
-	explicitlyEnabled(false),
-	implicitlyEnabled(true),
-	validation(PENDING),
-	config(addMeta(config, identifier))
-{
-	version = CModVersion::fromString(config["version"].String());
-	if(!config["compatibility"].isNull())
-	{
-		vcmiCompatibleMin = CModVersion::fromString(config["compatibility"]["min"].String());
-		vcmiCompatibleMax = CModVersion::fromString(config["compatibility"]["max"].String());
-	}
-
-	if (!config["language"].isNull())
-		baseLanguage = config["language"].String();
-	else
-		baseLanguage = "english";
-
-	loadLocalData(local);
-}
-
-JsonNode CModInfo::saveLocalData() const
-{
-	std::ostringstream stream;
-	stream << std::noshowbase << std::hex << std::setw(8) << std::setfill('0') << checksum;
-
-	JsonNode conf;
-	conf["active"].Bool() = explicitlyEnabled;
-	conf["validated"].Bool() = validation != FAILED;
-	conf["checksum"].String() = stream.str();
-	return conf;
-}
-
-std::string CModInfo::getModDir(const std::string & name)
-{
-	return "MODS/" + boost::algorithm::replace_all_copy(name, ".", "/MODS/");
-}
-
-std::string CModInfo::getModFile(const std::string & name)
-{
-	return getModDir(name) + "/mod.json";
-}
-
-void CModInfo::updateChecksum(ui32 newChecksum)
-{
-	// comment-out next line to force validation of all mods ignoring checksum
-	if (newChecksum != checksum)
-	{
-		checksum = newChecksum;
-		validation = PENDING;
-	}
-}
-
-void CModInfo::loadLocalData(const JsonNode & data)
-{
-	bool validated = false;
-	implicitlyEnabled = true;
-	explicitlyEnabled = !config["keepDisabled"].Bool();
-	checksum = 0;
-	if (data.getType() == JsonNode::JsonType::DATA_BOOL)
-	{
-		explicitlyEnabled = data.Bool();
-	}
-	if (data.getType() == JsonNode::JsonType::DATA_STRUCT)
-	{
-		explicitlyEnabled = data["active"].Bool();
-		validated = data["validated"].Bool();
-		checksum  = strtol(data["checksum"].String().c_str(), nullptr, 16);
-	}
-
-	//check compatibility
-	implicitlyEnabled &= (vcmiCompatibleMin.isNull() || CModVersion::GameVersion().compatible(vcmiCompatibleMin));
-	implicitlyEnabled &= (vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(CModVersion::GameVersion()));
-
-	if(!implicitlyEnabled)
-		logGlobal->warn("Mod %s is incompatible with current version of VCMI and cannot be enabled", name);
-
-	if (boost::iequals(config["modType"].String(), "translation")) // compatibility code - mods use "Translation" type at the moment
-	{
-		if (baseLanguage != VLC->generaltexth->getPreferredLanguage())
-		{
-			logGlobal->warn("Translation mod %s was not loaded: language mismatch!", name);
-			implicitlyEnabled = false;
-		}
-	}
-
-	if (isEnabled())
-		validation = validated ? PASSED : PENDING;
-	else
-		validation = validated ? PASSED : FAILED;
-}
-
-bool CModInfo::isEnabled() const
-{
-	return implicitlyEnabled && explicitlyEnabled;
-}
-
-void CModInfo::setEnabled(bool on)
-{
-	explicitlyEnabled = on;
-}
-
-CModHandler::CModHandler() : content(std::make_shared<CContentHandler>())
+CModHandler::CModHandler()
+	: content(std::make_shared<CContentHandler>())
+	, identifiers(std::make_unique<CIdentifierStorage>())
+	, coreMod(std::make_unique<CModInfo>())
 {
 	//TODO: moddable spell schools
 	for (auto i = 0; i < GameConstants::DEFAULT_SCHOOLS; ++i)
-		identifiers.registerObject(CModHandler::scopeBuiltin(), "spellSchool", SpellConfig::SCHOOL[i].jsonName, SpellConfig::SCHOOL[i].id);
+		identifiers->registerObject(ModScope::scopeBuiltin(), "spellSchool", SpellConfig::SCHOOL[i].jsonName, SpellConfig::SCHOOL[i].id);
 
-	identifiers.registerObject(CModHandler::scopeBuiltin(), "spellSchool", "any", SpellSchool(ESpellSchool::ANY));
+	identifiers->registerObject(ModScope::scopeBuiltin(), "spellSchool", "any", SpellSchool(ESpellSchool::ANY));
 
 	for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
 	{
-		identifiers.registerObject(CModHandler::scopeBuiltin(), "resource", GameConstants::RESOURCE_NAMES[i], i);
+		identifiers->registerObject(ModScope::scopeBuiltin(), "resource", GameConstants::RESOURCE_NAMES[i], i);
 	}
 
 	for(int i=0; i<GameConstants::PRIMARY_SKILLS; ++i)
 	{
-		identifiers.registerObject(CModHandler::scopeBuiltin(), "primSkill", PrimarySkill::names[i], i);
-		identifiers.registerObject(CModHandler::scopeBuiltin(), "primarySkill", PrimarySkill::names[i], i);
+		identifiers->registerObject(ModScope::scopeBuiltin(), "primSkill", PrimarySkill::names[i], i);
+		identifiers->registerObject(ModScope::scopeBuiltin(), "primarySkill", PrimarySkill::names[i], i);
 	}
 }
 
+CModHandler::~CModHandler() = default;
+
 // currentList is passed by value to get current list of depending mods
 bool CModHandler::hasCircularDependency(const TModID & modID, std::set<TModID> currentList) const
 {
@@ -834,35 +181,7 @@ std::vector<std::string> CModHandler::getModList(const std::string & path) const
 	return foundMods;
 }
 
-bool CModHandler::isScopeReserved(const TModID & scope)
-{
-	//following scopes are reserved - either in use by mod system or by filesystem
-	static const std::array<TModID, 9> reservedScopes = {
-		"core", "map", "game", "root", "saves", "config", "local", "initial", "mapEditor"
-	};
 
-	return std::find(reservedScopes.begin(), reservedScopes.end(), scope) != reservedScopes.end();
-}
-
-const TModID & CModHandler::scopeBuiltin()
-{
-	static const TModID scope = "core";
-	return scope;
-}
-
-const TModID & CModHandler::scopeGame()
-{
-	static const TModID scope = "game";
-	return scope;
-}
-
-const TModID & CModHandler::scopeMap()
-{
-	//TODO: implement accessing map dependencies for both H3 and VCMI maps
-	// for now, allow access to any identifiers
-	static const TModID scope = "game";
-	return scope;
-}
 
 void CModHandler::loadMods(const std::string & path, const std::string & parent, const JsonNode & modSettings, bool enableMods)
 {
@@ -875,7 +194,7 @@ void CModHandler::loadOneMod(std::string modName, const std::string & parent, co
 	boost::to_lower(modName);
 	std::string modFullName = parent.empty() ? modName : parent + '.' + modName;
 
-	if ( isScopeReserved(modFullName))
+	if ( ModScope::isScopeReserved(modFullName))
 	{
 		logMod->error("Can not load mod %s - this name is reserved for internal use!", modFullName);
 		return;
@@ -909,8 +228,8 @@ void CModHandler::loadMods(bool onlyEssential)
 		loadMods("", "", modConfig["activeMods"], true);
 	}
 
-	coreMod = CModInfo(CModHandler::scopeBuiltin(), modConfig[CModHandler::scopeBuiltin()], JsonNode(ResourceID("config/gameConfig.json")));
-	coreMod.name = "Original game files";
+	coreMod = std::make_unique<CModInfo>(ModScope::scopeBuiltin(), modConfig[ModScope::scopeBuiltin()], JsonNode(ResourceID("config/gameConfig.json")));
+	coreMod->name = "Original game files";
 }
 
 std::vector<std::string> CModHandler::getAllMods()
@@ -962,7 +281,7 @@ static ui32 calculateModChecksum(const std::string & modName, ISimpleResourceLoa
 
 	// second - add mod.json into checksum because filesystem does not contains this file
 	// FIXME: remove workaround for core mod
-	if (modName != CModHandler::scopeBuiltin())
+	if (modName != ModScope::scopeBuiltin())
 	{
 		ResourceID modConfFile(CModInfo::getModFile(modName), EResType::TEXT);
 		ui32 configChecksum = CResourceHandler::get("initial")->load(modConfFile)->calculateCRC32();
@@ -990,7 +309,7 @@ void CModHandler::loadModFilesystems()
 
 	activeMods = validateAndSortDependencies(activeMods);
 
-	coreMod.updateChecksum(calculateModChecksum(CModHandler::scopeBuiltin(), CResourceHandler::get(CModHandler::scopeBuiltin())));
+	coreMod->updateChecksum(calculateModChecksum(ModScope::scopeBuiltin(), CResourceHandler::get(ModScope::scopeBuiltin())));
 
 	for(std::string & modName : activeMods)
 	{
@@ -1038,7 +357,7 @@ std::set<TModID> CModHandler::getModDependencies(const TModID & modId, bool & is
 
 void CModHandler::initializeConfig()
 {
-	VLC->settingsHandler->load(coreMod.config["settings"]);
+	VLC->settingsHandler->load(coreMod->config["settings"]);
 
 	for(const TModID & modName : activeMods)
 	{
@@ -1048,6 +367,13 @@ void CModHandler::initializeConfig()
 	}
 }
 
+CModVersion CModHandler::getModVersion(TModID modName) const
+{
+	if (allMods.count(modName))
+		return allMods.at(modName).version;
+	return {};
+}
+
 bool CModHandler::validateTranslations(TModID modName) const
 {
 	bool result = true;
@@ -1112,12 +438,12 @@ void CModHandler::load()
 
 	// first - load virtual builtin mod that contains all data
 	// TODO? move all data into real mods? RoE, AB, SoD, WoG
-	content->preloadData(coreMod);
+	content->preloadData(*coreMod);
 	for(const TModID & modName : activeMods)
 		content->preloadData(allMods[modName]);
 	logMod->info("\tParsing mod data: %d ms", timer.getDiff());
 
-	content->load(coreMod);
+	content->load(*coreMod);
 	for(const TModID & modName : activeMods)
 		content->load(allMods[modName]);
 
@@ -1136,7 +462,7 @@ void CModHandler::load()
 
 	logMod->info("\tLoading mod data: %d ms", timer.getDiff());
 	VLC->creh->loadCrExpMod();
-	identifiers.finalize();
+	identifiers->finalize();
 	logMod->info("\tResolving identifiers: %d ms", timer.getDiff());
 
 	content->afterLoadFinalization();
@@ -1153,7 +479,7 @@ void CModHandler::afterLoad(bool onlyEssential)
 
 		modSettings["activeMods"].resolvePointer(pointer) = modEntry.second.saveLocalData();
 	}
-	modSettings[CModHandler::scopeBuiltin()] = coreMod.saveLocalData();
+	modSettings[ModScope::scopeBuiltin()] = coreMod->saveLocalData();
 
 	if(!onlyEssential)
 	{
@@ -1163,64 +489,28 @@ void CModHandler::afterLoad(bool onlyEssential)
 
 }
 
-std::string CModHandler::normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier)
-{
-	auto p = vstd::splitStringToPair(identifier, ':');
-
-	if(p.first.empty())
-		p.first = scope;
-
-	if(p.first == remoteScope)
-		p.first.clear();
-
-	return p.first.empty() ? p.second : p.first + ":" + p.second;
-}
-
-void CModHandler::parseIdentifier(const std::string & fullIdentifier, std::string & scope, std::string & type, std::string & identifier)
+void CModHandler::trySetActiveMods(const std::map<TModID, CModVersion> & modList)
 {
-	auto p = vstd::splitStringToPair(fullIdentifier, ':');
-
-	scope = p.first;
+	ModIncompatibility::ModList missingMods;
 
-	auto p2 = vstd::splitStringToPair(p.second, '.');
-
-	if(!p2.first.empty())
+	for(const auto & mod : modList)
 	{
-		type = p2.first;
-		identifier = p2.second;
-	}
-	else
-	{
-		type = p.second;
-		identifier.clear();
+		auto m = mod.first;
+		auto mver = mod.second;
+
+		if(allMods.count(m) && (allMods[m].version.isNull() || mver.isNull() || allMods[m].version.compatible(mver)))
+			allMods[m].setEnabled(true);
+		else
+			missingMods.emplace_back(m, mver.toString());
 	}
+
+	if(!missingMods.empty())
+		throw ModIncompatibility(std::move(missingMods));
 }
 
-std::string CModHandler::makeFullIdentifier(const std::string & scope, const std::string & type, const std::string & identifier)
+CIdentifierStorage & CModHandler::getIdentifiers()
 {
-	if(type.empty())
-		logGlobal->error("Full identifier (%s %s) requires type name", scope, identifier);
-
-	std::string actualScope = scope;
-	std::string actualName = identifier;
-
-	//ignore scope if identifier is scoped
-	auto scopeAndName = vstd::splitStringToPair(identifier, ':');
-
-	if(!scopeAndName.first.empty())
-	{
-		actualScope = scopeAndName.first;
-		actualName = scopeAndName.second;
-	}
-
-	if(actualScope.empty())
-	{
-		return actualName.empty() ? type : type + "." + actualName;
-	}
-	else
-	{
-		return actualName.empty() ? actualScope+ ":" + type : actualScope + ":" + type + "." + actualName;
-	}
+	return *identifiers;
 }
 
 VCMI_LIB_NAMESPACE_END

+ 22 - 283
lib/modding/CModHandler.h

@@ -9,15 +9,8 @@
  */
 #pragma once
 
-#include "JsonNode.h"
 #include "CModVersion.h"
 
-#ifdef __UCLIBC__
-#undef major
-#undef minor
-#undef patch
-#endif
-
 VCMI_LIB_NAMESPACE_BEGIN
 
 class CModHandler;
@@ -25,225 +18,17 @@ class CModIndentifier;
 class CModInfo;
 class JsonNode;
 class IHandlerBase;
-
-/// class that stores all object identifiers strings and maps them to numeric ID's
-/// if possible, objects ID's should be in format <type>.<name>, camelCase e.g. "creature.grandElf"
-class DLL_LINKAGE CIdentifierStorage
-{
-	enum ELoadingState
-	{
-		LOADING,
-		FINALIZING,
-		FINISHED
-	};
-
-	struct ObjectCallback // entry created on ID request
-	{
-		std::string localScope;  /// scope from which this ID was requested
-		std::string remoteScope; /// scope in which this object must be found
-		std::string type;        /// type, e.g. creature, faction, hero, etc
-		std::string name;        /// string ID
-		std::function<void(si32)> callback;
-		bool optional;
-
-		/// Builds callback from identifier in form "targetMod:type.name"
-		static ObjectCallback fromNameWithType(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback, bool optional);
-
-		/// Builds callback from identifier in form "targetMod:name"
-		static ObjectCallback fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional);
-
-	private:
-		ObjectCallback() = default;
-	};
-
-	struct ObjectData // entry created on ID registration
-	{
-		si32 id;
-		std::string scope; /// scope in which this ID located
-
-		bool operator==(const ObjectData & other) const
-		{
-			return id == other.id && scope == other.scope;
-		}
-
-		template <typename Handler> void serialize(Handler &h, const int version)
-		{
-			h & id;
-			h & scope;
-		}
-	};
-
-	std::multimap<std::string, ObjectData> registeredObjects;
-	std::vector<ObjectCallback> scheduledRequests;
-
-	ELoadingState state;
-
-	/// Check if identifier can be valid (camelCase, point as separator)
-	static void checkIdentifier(std::string & ID);
-
-	void requestIdentifier(ObjectCallback callback);
-	bool resolveIdentifier(const ObjectCallback & callback);
-	std::vector<ObjectData> getPossibleIdentifiers(const ObjectCallback & callback);
-public:
-	CIdentifierStorage();
-	virtual ~CIdentifierStorage() = default;
-	/// request identifier for specific object name.
-	/// Function callback will be called during ID resolution phase of loading
-	void requestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback);
-	///fullName = [remoteScope:]type.name
-	void requestIdentifier(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback);
-	void requestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback);
-	void requestIdentifier(const JsonNode & name, const std::function<void(si32)> & callback);
-
-	/// try to request ID. If ID with such name won't be loaded, callback function will not be called
-	void tryRequestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback);
-	void tryRequestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback);
-
-	/// get identifier immediately. If identifier is not know and not silent call will result in error message
-	std::optional<si32> getIdentifier(const std::string & scope, const std::string & type, const std::string & name, bool silent = false);
-	std::optional<si32> getIdentifier(const std::string & type, const JsonNode & name, bool silent = false);
-	std::optional<si32> getIdentifier(const JsonNode & name, bool silent = false);
-	std::optional<si32> getIdentifier(const std::string & scope, const std::string & fullName, bool silent = false);
-
-	/// registers new object
-	void registerObject(const std::string & scope, const std::string & type, const std::string & name, si32 identifier);
-
-	/// called at the very end of loading to check for any missing ID's
-	void finalize();
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & registeredObjects;
-		h & state;
-	}
-};
-
-/// internal type to handle loading of one data type (e.g. artifacts, creatures)
-class DLL_LINKAGE ContentTypeHandler
-{
-public:
-	struct ModInfo
-	{
-		/// mod data from this mod and for this mod
-		JsonNode modData;
-		/// mod data for this mod from other mods (patches)
-		JsonNode patches;
-	};
-	/// handler to which all data will be loaded
-	IHandlerBase * handler;
-	std::string objectName;
-
-	/// contains all loaded H3 data
-	std::vector<JsonNode> originalData;
-	std::map<std::string, ModInfo> modData;
-
-	ContentTypeHandler(IHandlerBase * handler, const std::string & objectName);
-
-	/// local version of methods in ContentHandler
-	/// returns true if loading was successful
-	bool preloadModData(const std::string & modName, const std::vector<std::string> & fileList, bool validate);
-	bool loadMod(const std::string & modName, bool validate);
-	void loadCustom();
-	void afterLoadFinalization();
-};
-
-/// class used to load all game data into handlers. Used only during loading
-class DLL_LINKAGE CContentHandler
-{
-	/// preloads all data from fileList as data from modName.
-	bool preloadModData(const std::string & modName, JsonNode modConfig, bool validate);
-
-	/// actually loads data in mod
-	bool loadMod(const std::string & modName, bool validate);
-
-	std::map<std::string, ContentTypeHandler> handlers;
-
-public:
-	void init();
-
-	/// preloads all data from fileList as data from modName.
-	void preloadData(CModInfo & mod);
-
-	/// actually loads data in mod
-	void load(CModInfo & mod);
-
-	void loadCustom();
-
-	/// all data was loaded, time for final validation / integration
-	void afterLoadFinalization();
-
-	const ContentTypeHandler & operator[] (const std::string & name) const;
-};
+class CIdentifierStorage;
+class CContentHandler;
+class ResourceID;
 
 using TModID = std::string;
 
-class DLL_LINKAGE CModInfo
-{
-public:
-	enum EValidationStatus
-	{
-		PENDING,
-		FAILED,
-		PASSED
-	};
-
-	/// identifier, identical to name of folder with mod
-	std::string identifier;
-
-	/// human-readable strings
-	std::string name;
-	std::string description;
-	
-	/// version of the mod
-	CModVersion version;
-
-	/// Base language of mod, all mod strings are assumed to be in this language
-	std::string baseLanguage;
-	
-	/// vcmi versions compatible with the mod
-
-	CModVersion vcmiCompatibleMin, vcmiCompatibleMax;
-
-	/// list of mods that should be loaded before this one
-	std::set <TModID> dependencies;
-
-	/// list of mods that can't be used in the same time as this one
-	std::set <TModID> conflicts;
-
-	/// CRC-32 checksum of the mod
-	ui32 checksum;
-
-	EValidationStatus validation;
-
-	JsonNode config;
-
-	CModInfo();
-	CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config);
-
-	JsonNode saveLocalData() const;
-	void updateChecksum(ui32 newChecksum);
-
-	bool isEnabled() const;
-	void setEnabled(bool on);
-
-	static std::string getModDir(const std::string & name);
-	static std::string getModFile(const std::string & name);
-
-private:
-	/// true if mod is enabled by user, e.g. in Launcher UI
-	bool explicitlyEnabled;
-
-	/// true if mod can be loaded - compatible and has no missing deps
-	bool implicitlyEnabled;
-
-	void loadLocalData(const JsonNode & data);
-};
-
-class DLL_LINKAGE CModHandler
+class DLL_LINKAGE CModHandler : boost::noncopyable
 {
 	std::map <TModID, CModInfo> allMods;
 	std::vector <TModID> activeMods;//active mods, in order in which they were loaded
-	CModInfo coreMod;
+	std::unique_ptr<CModInfo> coreMod;
 
 	bool hasCircularDependency(const TModID & mod, std::set<TModID> currentList = std::set<TModID>()) const;
 
@@ -263,51 +48,18 @@ class DLL_LINKAGE CModHandler
 	void loadTranslation(const TModID & modName);
 
 	bool validateTranslations(TModID modName) const;
-public:
 
-	/// returns true if scope is reserved for internal use and can not be used by mods
-	static bool isScopeReserved(const TModID & scope);
+	CModVersion getModVersion(TModID modName) const;
 
-	/// reserved scope name for referencing built-in (e.g. H3) objects
-	static const TModID & scopeBuiltin();
+	/// Attempt to set active mods according to provided list of mods from save, throws on failure
+	void trySetActiveMods(const std::map<TModID, CModVersion> & modList);
 
-	/// reserved scope name for accessing objects from any loaded mod
-	static const TModID & scopeGame();
+	std::unique_ptr<CIdentifierStorage> identifiers;
 
-	/// reserved scope name for accessing object for map loading
-	static const TModID & scopeMap();
-
-	class DLL_LINKAGE Incompatibility: public std::exception
-	{
-	public:
-		using StringPair = std::pair<const std::string, const std::string>;
-		using ModList = std::list<StringPair>;
-		
-		Incompatibility(ModList && _missingMods):
-			missingMods(std::move(_missingMods))
-		{
-			std::ostringstream _ss;
-			for(const auto & m : missingMods)
-				_ss << m.first << ' ' << m.second << std::endl;
-			message = _ss.str();
-		}
-		
-		const char * what() const noexcept override
-		{
-			return message.c_str();
-		}
-		
-	private:
-		//list of mods required to load the game
-		// first: mod name
-		// second: mod version
-		const ModList missingMods;
-		std::string message;
-	};
-
-	CIdentifierStorage identifiers;
+public:
+	std::shared_ptr<CContentHandler> content; //(!)Do not serialize FIXME: make private
 
-	std::shared_ptr<CContentHandler> content; //(!)Do not serialize
+	CIdentifierStorage & getIdentifiers();
 
 	/// receives list of available mods and trying to load mod.json from all of them
 	void initializeConfig();
@@ -332,13 +84,7 @@ public:
 	void afterLoad(bool onlyEssential);
 
 	CModHandler();
-	virtual ~CModHandler() = default;
-
-	static std::string normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier);
-
-	static void parseIdentifier(const std::string & fullIdentifier, std::string & scope, std::string & type, std::string & identifier);
-
-	static std::string makeFullIdentifier(const std::string & scope, const std::string & type, const std::string & identifier);
+	virtual ~CModHandler();
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -346,30 +92,23 @@ public:
 		{
 			h & activeMods;
 			for(const auto & m : activeMods)
-				h & allMods[m].version;
+			{
+				CModVersion version = getModVersion(m);
+				h & version;
+			}
 		}
 		else
 		{
 			loadMods();
 			std::vector<TModID> newActiveMods;
+			std::map<TModID, CModVersion> modVersions;
 			h & newActiveMods;
-			
-			Incompatibility::ModList missingMods;
+
 			for(const auto & m : newActiveMods)
+				h & modVersions[m];
+
+			trySetActiveMods(modVersions);
 
-			{
-				CModVersion mver;
-				h & mver;
-				
-				if(allMods.count(m) && (allMods[m].version.isNull() || mver.isNull() || allMods[m].version.compatible(mver)))
-					allMods[m].setEnabled(true);
-				else
-					missingMods.emplace_back(m, mver.toString());
-			}
-			
-			if(!missingMods.empty())
-				throw Incompatibility(std::move(missingMods));
-			
 			std::swap(activeMods, newActiveMods);
 		}
 

+ 141 - 0
lib/modding/CModInfo.cpp

@@ -0,0 +1,141 @@
+/*
+ * CModInfo.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "CModInfo.h"
+
+#include "../CGeneralTextHandler.h"
+#include "../VCMI_Lib.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+static JsonNode addMeta(JsonNode config, const std::string & meta)
+{
+	config.setMeta(meta);
+	return config;
+}
+
+CModInfo::CModInfo():
+	checksum(0),
+	explicitlyEnabled(false),
+	implicitlyEnabled(true),
+	validation(PENDING)
+{
+
+}
+
+CModInfo::CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config):
+	identifier(identifier),
+	name(config["name"].String()),
+	description(config["description"].String()),
+	dependencies(config["depends"].convertTo<std::set<std::string>>()),
+	conflicts(config["conflicts"].convertTo<std::set<std::string>>()),
+	checksum(0),
+	explicitlyEnabled(false),
+	implicitlyEnabled(true),
+	validation(PENDING),
+	config(addMeta(config, identifier))
+{
+	version = CModVersion::fromString(config["version"].String());
+	if(!config["compatibility"].isNull())
+	{
+		vcmiCompatibleMin = CModVersion::fromString(config["compatibility"]["min"].String());
+		vcmiCompatibleMax = CModVersion::fromString(config["compatibility"]["max"].String());
+	}
+
+	if (!config["language"].isNull())
+		baseLanguage = config["language"].String();
+	else
+		baseLanguage = "english";
+
+	loadLocalData(local);
+}
+
+JsonNode CModInfo::saveLocalData() const
+{
+	std::ostringstream stream;
+	stream << std::noshowbase << std::hex << std::setw(8) << std::setfill('0') << checksum;
+
+	JsonNode conf;
+	conf["active"].Bool() = explicitlyEnabled;
+	conf["validated"].Bool() = validation != FAILED;
+	conf["checksum"].String() = stream.str();
+	return conf;
+}
+
+std::string CModInfo::getModDir(const std::string & name)
+{
+	return "MODS/" + boost::algorithm::replace_all_copy(name, ".", "/MODS/");
+}
+
+std::string CModInfo::getModFile(const std::string & name)
+{
+	return getModDir(name) + "/mod.json";
+}
+
+void CModInfo::updateChecksum(ui32 newChecksum)
+{
+	// comment-out next line to force validation of all mods ignoring checksum
+	if (newChecksum != checksum)
+	{
+		checksum = newChecksum;
+		validation = PENDING;
+	}
+}
+
+void CModInfo::loadLocalData(const JsonNode & data)
+{
+	bool validated = false;
+	implicitlyEnabled = true;
+	explicitlyEnabled = !config["keepDisabled"].Bool();
+	checksum = 0;
+	if (data.getType() == JsonNode::JsonType::DATA_BOOL)
+	{
+		explicitlyEnabled = data.Bool();
+	}
+	if (data.getType() == JsonNode::JsonType::DATA_STRUCT)
+	{
+		explicitlyEnabled = data["active"].Bool();
+		validated = data["validated"].Bool();
+		checksum  = strtol(data["checksum"].String().c_str(), nullptr, 16);
+	}
+
+	//check compatibility
+	implicitlyEnabled &= (vcmiCompatibleMin.isNull() || CModVersion::GameVersion().compatible(vcmiCompatibleMin));
+	implicitlyEnabled &= (vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(CModVersion::GameVersion()));
+
+	if(!implicitlyEnabled)
+		logGlobal->warn("Mod %s is incompatible with current version of VCMI and cannot be enabled", name);
+
+	if (boost::iequals(config["modType"].String(), "translation")) // compatibility code - mods use "Translation" type at the moment
+	{
+		if (baseLanguage != VLC->generaltexth->getPreferredLanguage())
+		{
+			logGlobal->warn("Translation mod %s was not loaded: language mismatch!", name);
+			implicitlyEnabled = false;
+		}
+	}
+
+	if (isEnabled())
+		validation = validated ? PASSED : PENDING;
+	else
+		validation = validated ? PASSED : FAILED;
+}
+
+bool CModInfo::isEnabled() const
+{
+	return implicitlyEnabled && explicitlyEnabled;
+}
+
+void CModInfo::setEnabled(bool on)
+{
+	explicitlyEnabled = on;
+}
+
+VCMI_LIB_NAMESPACE_END

+ 80 - 0
lib/modding/CModInfo.h

@@ -0,0 +1,80 @@
+/*
+ * CModInfo.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../JsonNode.h"
+#include "CModVersion.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+using TModID = std::string;
+
+class DLL_LINKAGE CModInfo
+{
+public:
+	enum EValidationStatus
+	{
+		PENDING,
+		FAILED,
+		PASSED
+	};
+
+	/// identifier, identical to name of folder with mod
+	std::string identifier;
+
+	/// human-readable strings
+	std::string name;
+	std::string description;
+
+	/// version of the mod
+	CModVersion version;
+
+	/// Base language of mod, all mod strings are assumed to be in this language
+	std::string baseLanguage;
+
+	/// vcmi versions compatible with the mod
+	CModVersion vcmiCompatibleMin, vcmiCompatibleMax;
+
+	/// list of mods that should be loaded before this one
+	std::set <TModID> dependencies;
+
+	/// list of mods that can't be used in the same time as this one
+	std::set <TModID> conflicts;
+
+	/// CRC-32 checksum of the mod
+	ui32 checksum;
+
+	EValidationStatus validation;
+
+	JsonNode config;
+
+	CModInfo();
+	CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config);
+
+	JsonNode saveLocalData() const;
+	void updateChecksum(ui32 newChecksum);
+
+	bool isEnabled() const;
+	void setEnabled(bool on);
+
+	static std::string getModDir(const std::string & name);
+	static std::string getModFile(const std::string & name);
+
+private:
+	/// true if mod is enabled by user, e.g. in Launcher UI
+	bool explicitlyEnabled;
+
+	/// true if mod can be loaded - compatible and has no missing deps
+	bool implicitlyEnabled;
+
+	void loadLocalData(const JsonNode & data);
+};
+
+VCMI_LIB_NAMESPACE_END

+ 6 - 0
lib/modding/CModVersion.h

@@ -10,6 +10,12 @@
 
 #pragma once
 
+#ifdef __UCLIBC__
+#undef major
+#undef minor
+#undef patch
+#endif
+
 VCMI_LIB_NAMESPACE_BEGIN
 
 struct DLL_LINKAGE CModVersion

+ 249 - 0
lib/modding/ContentTypeHandler.cpp

@@ -0,0 +1,249 @@
+/*
+ * ContentTypeHandler.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "ContentTypeHandler.h"
+
+#include "CModHandler.h"
+#include "CModInfo.h"
+#include "ModScope.h"
+
+#include "../BattleFieldHandler.h"
+#include "../CArtHandler.h"
+#include "../CCreatureHandler.h"
+#include "../CGeneralTextHandler.h"
+#include "../CHeroHandler.h"
+#include "../CSkillHandler.h"
+#include "../CStopWatch.h"
+#include "../CTownHandler.h"
+#include "../GameSettings.h"
+#include "../IHandlerBase.h"
+#include "../Languages.h"
+#include "../ObstacleHandler.h"
+#include "../RiverHandler.h"
+#include "../RoadHandler.h"
+#include "../ScriptHandler.h"
+#include "../StringConstants.h"
+#include "../TerrainHandler.h"
+#include "../mapObjectConstructors/CObjectClassesHandler.h"
+#include "../rmg/CRmgTemplateStorage.h"
+#include "../spells/CSpellHandler.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string & objectName):
+	handler(handler),
+	objectName(objectName),
+	originalData(handler->loadLegacyData())
+{
+	for(auto & node : originalData)
+	{
+		node.setMeta(ModScope::scopeBuiltin());
+	}
+}
+
+bool ContentTypeHandler::preloadModData(const std::string & modName, const std::vector<std::string> & fileList, bool validate)
+{
+	bool result = false;
+	JsonNode data = JsonUtils::assembleFromFiles(fileList, result);
+	data.setMeta(modName);
+
+	ModInfo & modInfo = modData[modName];
+
+	for(auto entry : data.Struct())
+	{
+		size_t colon = entry.first.find(':');
+
+		if (colon == std::string::npos)
+		{
+			// normal object, local to this mod
+			modInfo.modData[entry.first].swap(entry.second);
+		}
+		else
+		{
+			std::string remoteName = entry.first.substr(0, colon);
+			std::string objectName = entry.first.substr(colon + 1);
+
+			// patching this mod? Send warning and continue - this situation can be handled normally
+			if (remoteName == modName)
+				logMod->warn("Redundant namespace definition for %s", objectName);
+
+			logMod->trace("Patching object %s (%s) from %s", objectName, remoteName, modName);
+			JsonNode & remoteConf = modData[remoteName].patches[objectName];
+
+			JsonUtils::merge(remoteConf, entry.second);
+		}
+	}
+	return result;
+}
+
+bool ContentTypeHandler::loadMod(const std::string & modName, bool validate)
+{
+	ModInfo & modInfo = modData[modName];
+	bool result = true;
+
+	auto performValidate = [&,this](JsonNode & data, const std::string & name){
+		handler->beforeValidate(data);
+		if (validate)
+			result &= JsonUtils::validate(data, "vcmi:" + objectName, name);
+	};
+
+	// apply patches
+	if (!modInfo.patches.isNull())
+		JsonUtils::merge(modInfo.modData, modInfo.patches);
+
+	for(auto & entry : modInfo.modData.Struct())
+	{
+		const std::string & name = entry.first;
+		JsonNode & data = entry.second;
+
+		if (data.meta != modName)
+			logMod->warn("Mod %s is attempting to inject object %s into mod %s! This may not be supported in future versions!", data.meta, name, modName);
+
+		if (vstd::contains(data.Struct(), "index") && !data["index"].isNull())
+		{
+			if (modName != "core")
+				logMod->warn("Mod %s is attempting to load original data! This should be reserved for built-in mod.", modName);
+
+			// try to add H3 object data
+			size_t index = static_cast<size_t>(data["index"].Float());
+
+			if(originalData.size() > index)
+			{
+				logMod->trace("found original data in loadMod(%s) at index %d", name, index);
+				JsonUtils::merge(originalData[index], data);
+				std::swap(originalData[index], data);
+				originalData[index].clear(); // do not use same data twice (same ID)
+			}
+			else
+			{
+				logMod->trace("no original data in loadMod(%s) at index %d", name, index);
+			}
+			performValidate(data, name);
+			handler->loadObject(modName, name, data, index);
+		}
+		else
+		{
+			// normal new object
+			logMod->trace("no index in loadMod(%s)", name);
+			performValidate(data,name);
+			handler->loadObject(modName, name, data);
+		}
+	}
+	return result;
+}
+
+void ContentTypeHandler::loadCustom()
+{
+	handler->loadCustom();
+}
+
+void ContentTypeHandler::afterLoadFinalization()
+{
+	handler->afterLoadFinalization();
+}
+
+void CContentHandler::init()
+{
+	handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass")));
+	handlers.insert(std::make_pair("artifacts", ContentTypeHandler(VLC->arth, "artifact")));
+	handlers.insert(std::make_pair("creatures", ContentTypeHandler(VLC->creh, "creature")));
+	handlers.insert(std::make_pair("factions", ContentTypeHandler(VLC->townh, "faction")));
+	handlers.insert(std::make_pair("objects", ContentTypeHandler(VLC->objtypeh, "object")));
+	handlers.insert(std::make_pair("heroes", ContentTypeHandler(VLC->heroh, "hero")));
+	handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
+	handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill")));
+	handlers.insert(std::make_pair("templates", ContentTypeHandler(VLC->tplh, "template")));
+#if SCRIPTING_ENABLED
+	handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script")));
+#endif
+	handlers.insert(std::make_pair("battlefields", ContentTypeHandler(VLC->battlefieldsHandler, "battlefield")));
+	handlers.insert(std::make_pair("terrains", ContentTypeHandler(VLC->terrainTypeHandler, "terrain")));
+	handlers.insert(std::make_pair("rivers", ContentTypeHandler(VLC->riverTypeHandler, "river")));
+	handlers.insert(std::make_pair("roads", ContentTypeHandler(VLC->roadTypeHandler, "road")));
+	handlers.insert(std::make_pair("obstacles", ContentTypeHandler(VLC->obstacleHandler, "obstacle")));
+	//TODO: any other types of moddables?
+}
+
+bool CContentHandler::preloadModData(const std::string & modName, JsonNode modConfig, bool validate)
+{
+	bool result = true;
+	for(auto & handler : handlers)
+	{
+		result &= handler.second.preloadModData(modName, modConfig[handler.first].convertTo<std::vector<std::string> >(), validate);
+	}
+	return result;
+}
+
+bool CContentHandler::loadMod(const std::string & modName, bool validate)
+{
+	bool result = true;
+	for(auto & handler : handlers)
+	{
+		result &= handler.second.loadMod(modName, validate);
+	}
+	return result;
+}
+
+void CContentHandler::loadCustom()
+{
+	for(auto & handler : handlers)
+	{
+		handler.second.loadCustom();
+	}
+}
+
+void CContentHandler::afterLoadFinalization()
+{
+	for(auto & handler : handlers)
+	{
+		handler.second.afterLoadFinalization();
+	}
+}
+
+void CContentHandler::preloadData(CModInfo & mod)
+{
+	bool validate = (mod.validation != CModInfo::PASSED);
+
+	// print message in format [<8-symbols checksum>] <modname>
+	logMod->info("\t\t[%08x]%s", mod.checksum, mod.name);
+
+	if (validate && mod.identifier != ModScope::scopeBuiltin())
+	{
+		if (!JsonUtils::validate(mod.config, "vcmi:mod", mod.identifier))
+			mod.validation = CModInfo::FAILED;
+	}
+	if (!preloadModData(mod.identifier, mod.config, validate))
+		mod.validation = CModInfo::FAILED;
+}
+
+void CContentHandler::load(CModInfo & mod)
+{
+	bool validate = (mod.validation != CModInfo::PASSED);
+
+	if (!loadMod(mod.identifier, validate))
+		mod.validation = CModInfo::FAILED;
+
+	if (validate)
+	{
+		if (mod.validation != CModInfo::FAILED)
+			logMod->info("\t\t[DONE] %s", mod.name);
+		else
+			logMod->error("\t\t[FAIL] %s", mod.name);
+	}
+	else
+		logMod->info("\t\t[SKIP] %s", mod.name);
+}
+
+const ContentTypeHandler & CContentHandler::operator[](const std::string & name) const
+{
+	return handlers.at(name);
+}
+
+VCMI_LIB_NAMESPACE_END

+ 77 - 0
lib/modding/ContentTypeHandler.h

@@ -0,0 +1,77 @@
+/*
+ * ContentTypeHandler.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../JsonNode.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class IHandlerBase;
+class CModInfo;
+
+/// internal type to handle loading of one data type (e.g. artifacts, creatures)
+class DLL_LINKAGE ContentTypeHandler
+{
+public:
+	struct ModInfo
+	{
+		/// mod data from this mod and for this mod
+		JsonNode modData;
+		/// mod data for this mod from other mods (patches)
+		JsonNode patches;
+	};
+	/// handler to which all data will be loaded
+	IHandlerBase * handler;
+	std::string objectName;
+
+	/// contains all loaded H3 data
+	std::vector<JsonNode> originalData;
+	std::map<std::string, ModInfo> modData;
+
+	ContentTypeHandler(IHandlerBase * handler, const std::string & objectName);
+
+	/// local version of methods in ContentHandler
+	/// returns true if loading was successful
+	bool preloadModData(const std::string & modName, const std::vector<std::string> & fileList, bool validate);
+	bool loadMod(const std::string & modName, bool validate);
+	void loadCustom();
+	void afterLoadFinalization();
+};
+
+/// class used to load all game data into handlers. Used only during loading
+class DLL_LINKAGE CContentHandler
+{
+	/// preloads all data from fileList as data from modName.
+	bool preloadModData(const std::string & modName, JsonNode modConfig, bool validate);
+
+	/// actually loads data in mod
+	bool loadMod(const std::string & modName, bool validate);
+
+	std::map<std::string, ContentTypeHandler> handlers;
+
+public:
+	void init();
+
+	/// preloads all data from fileList as data from modName.
+	void preloadData(CModInfo & mod);
+
+	/// actually loads data in mod
+	void load(CModInfo & mod);
+
+	void loadCustom();
+
+	/// all data was loaded, time for final validation / integration
+	void afterLoadFinalization();
+
+	const ContentTypeHandler & operator[] (const std::string & name) const;
+};
+
+
+VCMI_LIB_NAMESPACE_END

+ 339 - 0
lib/modding/IdentifierStorage.cpp

@@ -0,0 +1,339 @@
+/*
+ * IdentifierStorage.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "IdentifierStorage.h"
+
+#include "CModHandler.h"
+#include "ModScope.h"
+
+#include "../JsonNode.h"
+#include "../VCMI_Lib.h"
+
+#include <vstd/StringUtils.h>
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+CIdentifierStorage::CIdentifierStorage():
+	state(LOADING)
+{
+}
+
+void CIdentifierStorage::checkIdentifier(std::string & ID)
+{
+	if (boost::algorithm::ends_with(ID, "."))
+		logMod->warn("BIG WARNING: identifier %s seems to be broken!", ID);
+	else
+	{
+		size_t pos = 0;
+		do
+		{
+			if (std::tolower(ID[pos]) != ID[pos] ) //Not in camelCase
+			{
+				logMod->warn("Warning: identifier %s is not in camelCase!", ID);
+				ID[pos] = std::tolower(ID[pos]);// Try to fix the ID
+			}
+			pos = ID.find('.', pos);
+		}
+		while(pos++ != std::string::npos);
+	}
+}
+
+void CIdentifierStorage::requestIdentifier(ObjectCallback callback) const
+{
+	checkIdentifier(callback.type);
+	checkIdentifier(callback.name);
+
+	assert(!callback.localScope.empty());
+
+	if (state != FINISHED) // enqueue request if loading is still in progress
+		scheduledRequests.push_back(callback);
+	else // execute immediately for "late" requests
+		resolveIdentifier(callback);
+}
+
+CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameWithType(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback, bool optional)
+{
+	assert(!scope.empty());
+
+	auto scopeAndFullName = vstd::splitStringToPair(fullName, ':');
+	auto typeAndName = vstd::splitStringToPair(scopeAndFullName.second, '.');
+
+	if (scope == scopeAndFullName.first)
+		logMod->debug("Target scope for identifier '%s' is redundant! Identifier already defined in mod '%s'", fullName, scope);
+
+	ObjectCallback result;
+	result.localScope = scope;
+	result.remoteScope = scopeAndFullName.first;
+	result.type = typeAndName.first;
+	result.name = typeAndName.second;
+	result.callback = callback;
+	result.optional = optional;
+	return result;
+}
+
+CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional)
+{
+	assert(!scope.empty());
+
+	auto scopeAndFullName = vstd::splitStringToPair(fullName, ':');
+	auto typeAndName = vstd::splitStringToPair(scopeAndFullName.second, '.');
+
+	if(!typeAndName.first.empty())
+	{
+		if (typeAndName.first != type)
+			logMod->error("Identifier '%s' from mod '%s' requested with different type! Type '%s' expected!", fullName, scope, type);
+		else
+			logMod->debug("Target type for identifier '%s' defined in mod '%s' is redundant!", fullName, scope);
+	}
+
+	if (scope == scopeAndFullName.first)
+		logMod->debug("Target scope for identifier '%s' is redundant! Identifier already defined in mod '%s'", fullName, scope);
+
+	ObjectCallback result;
+	result.localScope = scope;
+	result.remoteScope = scopeAndFullName.first;
+	result.type = type;
+	result.name = typeAndName.second;
+	result.callback = callback;
+	result.optional = optional;
+	return result;
+}
+
+void CIdentifierStorage::requestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const
+{
+	requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, false));
+}
+
+void CIdentifierStorage::requestIdentifier(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback) const
+{
+	requestIdentifier(ObjectCallback::fromNameWithType(scope, fullName, callback, false));
+}
+
+void CIdentifierStorage::requestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const
+{
+	requestIdentifier(ObjectCallback::fromNameAndType(name.meta, type, name.String(), callback, false));
+}
+
+void CIdentifierStorage::requestIdentifier(const JsonNode & name, const std::function<void(si32)> & callback) const
+{
+	requestIdentifier(ObjectCallback::fromNameWithType(name.meta, name.String(), callback, false));
+}
+
+void CIdentifierStorage::tryRequestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const
+{
+	requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, true));
+}
+
+void CIdentifierStorage::tryRequestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const
+{
+	requestIdentifier(ObjectCallback::fromNameAndType(name.meta, type, name.String(), callback, true));
+}
+
+std::optional<si32> CIdentifierStorage::getIdentifier(const std::string & scope, const std::string & type, const std::string & name, bool silent) const
+{
+	auto idList = getPossibleIdentifiers(ObjectCallback::fromNameAndType(scope, type, name, std::function<void(si32)>(), silent));
+
+	if (idList.size() == 1)
+		return idList.front().id;
+	if (!silent)
+		logMod->error("Failed to resolve identifier %s of type %s from mod %s", name , type ,scope);
+
+	return std::optional<si32>();
+}
+
+std::optional<si32> CIdentifierStorage::getIdentifier(const std::string & type, const JsonNode & name, bool silent) const
+{
+	auto idList = getPossibleIdentifiers(ObjectCallback::fromNameAndType(name.meta, type, name.String(), std::function<void(si32)>(), silent));
+
+	if (idList.size() == 1)
+		return idList.front().id;
+	if (!silent)
+		logMod->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), type, name.meta);
+
+	return std::optional<si32>();
+}
+
+std::optional<si32> CIdentifierStorage::getIdentifier(const JsonNode & name, bool silent) const
+{
+	auto idList = getPossibleIdentifiers(ObjectCallback::fromNameWithType(name.meta, name.String(), std::function<void(si32)>(), silent));
+
+	if (idList.size() == 1)
+		return idList.front().id;
+	if (!silent)
+		logMod->error("Failed to resolve identifier %s from mod %s", name.String(), name.meta);
+
+	return std::optional<si32>();
+}
+
+std::optional<si32> CIdentifierStorage::getIdentifier(const std::string & scope, const std::string & fullName, bool silent) const
+{
+	auto idList = getPossibleIdentifiers(ObjectCallback::fromNameWithType(scope, fullName, std::function<void(si32)>(), silent));
+
+	if (idList.size() == 1)
+		return idList.front().id;
+	if (!silent)
+		logMod->error("Failed to resolve identifier %s from mod %s", fullName, scope);
+
+	return std::optional<si32>();
+}
+
+void CIdentifierStorage::registerObject(const std::string & scope, const std::string & type, const std::string & name, si32 identifier)
+{
+	ObjectData data;
+	data.scope = scope;
+	data.id = identifier;
+
+	std::string fullID = type + '.' + name;
+	checkIdentifier(fullID);
+
+	std::pair<const std::string, ObjectData> mapping = std::make_pair(fullID, data);
+	if(!vstd::containsMapping(registeredObjects, mapping))
+	{
+		logMod->trace("registered %s as %s:%s", fullID, scope, identifier);
+		registeredObjects.insert(mapping);
+	}
+}
+
+std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdentifiers(const ObjectCallback & request) const
+{
+	std::set<std::string> allowedScopes;
+	bool isValidScope = true;
+
+	// called have not specified destination mod explicitly
+	if (request.remoteScope.empty())
+	{
+		// special scope that should have access to all in-game objects
+		if (request.localScope == ModScope::scopeGame())
+		{
+			for(const auto & modName : VLC->modh->getActiveMods())
+				allowedScopes.insert(modName);
+		}
+
+		// normally ID's from all required mods, own mod and virtual built-in mod are allowed
+		else if(request.localScope != ModScope::scopeBuiltin() && !request.localScope.empty())
+		{
+			allowedScopes = VLC->modh->getModDependencies(request.localScope, isValidScope);
+
+			if(!isValidScope)
+				return std::vector<ObjectData>();
+
+			allowedScopes.insert(request.localScope);
+		}
+
+		// all mods can access built-in mod
+		allowedScopes.insert(ModScope::scopeBuiltin());
+	}
+	else
+	{
+		//if destination mod was specified explicitly, restrict lookup to this mod
+		if(request.remoteScope == ModScope::scopeBuiltin() )
+		{
+			//built-in mod is an implicit dependency for all mods, allow access into it
+			allowedScopes.insert(request.remoteScope);
+		}
+		else if ( request.localScope == ModScope::scopeGame() )
+		{
+			// allow access, this is special scope that should have access to all in-game objects
+			allowedScopes.insert(request.remoteScope);
+		}
+		else if(request.remoteScope == request.localScope )
+		{
+			// allow self-access
+			allowedScopes.insert(request.remoteScope);
+		}
+		else
+		{
+			// allow access only if mod is in our dependencies
+			auto myDeps = VLC->modh->getModDependencies(request.localScope, isValidScope);
+
+			if(!isValidScope)
+				return std::vector<ObjectData>();
+
+			if(myDeps.count(request.remoteScope))
+				allowedScopes.insert(request.remoteScope);
+		}
+	}
+
+	std::string fullID = request.type + '.' + request.name;
+
+	auto entries = registeredObjects.equal_range(fullID);
+	if (entries.first != entries.second)
+	{
+		std::vector<ObjectData> locatedIDs;
+
+		for (auto it = entries.first; it != entries.second; it++)
+		{
+			if (vstd::contains(allowedScopes, it->second.scope))
+			{
+				locatedIDs.push_back(it->second);
+			}
+		}
+		return locatedIDs;
+	}
+	return std::vector<ObjectData>();
+}
+
+bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request) const
+{
+	auto identifiers = getPossibleIdentifiers(request);
+	if (identifiers.size() == 1) // normally resolved ID
+	{
+		request.callback(identifiers.front().id);
+		return true;
+	}
+
+	if (request.optional && identifiers.empty()) // failed to resolve optinal ID
+	{
+		return true;
+	}
+
+	// error found. Try to generate some debug info
+	if(identifiers.empty())
+		logMod->error("Unknown identifier!");
+	else
+		logMod->error("Ambiguous identifier request!");
+
+	 logMod->error("Request for %s.%s from mod %s", request.type, request.name, request.localScope);
+
+	for(const auto & id : identifiers)
+	{
+		logMod->error("\tID is available in mod %s", id.scope);
+	}
+	return false;
+}
+
+void CIdentifierStorage::finalize()
+{
+	state = FINALIZING;
+	bool errorsFound = false;
+
+	while ( !scheduledRequests.empty() )
+	{
+		// 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)
+	{
+		for(const auto & object : registeredObjects)
+		{
+			logMod->trace("%s : %s -> %d", object.second.scope, object.first, object.second.id);
+		}
+		logMod->error("All known identifiers were dumped into log file");
+	}
+	assert(errorsFound == false);
+	state = FINISHED;
+}
+
+VCMI_LIB_NAMESPACE_END

+ 110 - 0
lib/modding/IdentifierStorage.h

@@ -0,0 +1,110 @@
+/*
+ * IdentifierStorage.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class JsonNode;
+
+/// class that stores all object identifiers strings and maps them to numeric ID's
+/// if possible, objects ID's should be in format <type>.<name>, camelCase e.g. "creature.grandElf"
+class DLL_LINKAGE CIdentifierStorage
+{
+	enum ELoadingState
+	{
+		LOADING,
+		FINALIZING,
+		FINISHED
+	};
+
+	struct ObjectCallback // entry created on ID request
+	{
+		std::string localScope;  /// scope from which this ID was requested
+		std::string remoteScope; /// scope in which this object must be found
+		std::string type;        /// type, e.g. creature, faction, hero, etc
+		std::string name;        /// string ID
+		std::function<void(si32)> callback;
+		bool optional;
+
+		/// Builds callback from identifier in form "targetMod:type.name"
+		static ObjectCallback fromNameWithType(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback, bool optional);
+
+		/// Builds callback from identifier in form "targetMod:name"
+		static ObjectCallback fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional);
+
+	private:
+		ObjectCallback() = default;
+	};
+
+	struct ObjectData // entry created on ID registration
+	{
+		si32 id;
+		std::string scope; /// scope in which this ID located
+
+		bool operator==(const ObjectData & other) const
+		{
+			return id == other.id && scope == other.scope;
+		}
+
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & id;
+			h & scope;
+		}
+	};
+
+	std::multimap<std::string, ObjectData> registeredObjects;
+	mutable std::vector<ObjectCallback> scheduledRequests;
+
+	ELoadingState state;
+
+	/// Check if identifier can be valid (camelCase, point as separator)
+	static void checkIdentifier(std::string & ID);
+
+	void requestIdentifier(ObjectCallback callback) const;
+	bool resolveIdentifier(const ObjectCallback & callback) const;
+	std::vector<ObjectData> getPossibleIdentifiers(const ObjectCallback & callback) const;
+
+public:
+	CIdentifierStorage();
+	virtual ~CIdentifierStorage() = default;
+
+	/// request identifier for specific object name.
+	/// Function callback will be called during ID resolution phase of loading
+	void requestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const;
+	///fullName = [remoteScope:]type.name
+	void requestIdentifier(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback) const;
+	void requestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const;
+	void requestIdentifier(const JsonNode & name, const std::function<void(si32)> & callback) const;
+
+	/// try to request ID. If ID with such name won't be loaded, callback function will not be called
+	void tryRequestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const;
+	void tryRequestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const;
+
+	/// get identifier immediately. If identifier is not know and not silent call will result in error message
+	std::optional<si32> getIdentifier(const std::string & scope, const std::string & type, const std::string & name, bool silent = false) const;
+	std::optional<si32> getIdentifier(const std::string & type, const JsonNode & name, bool silent = false) const;
+	std::optional<si32> getIdentifier(const JsonNode & name, bool silent = false) const;
+	std::optional<si32> getIdentifier(const std::string & scope, const std::string & fullName, bool silent = false) const;
+
+	/// registers new object
+	void registerObject(const std::string & scope, const std::string & type, const std::string & name, si32 identifier);
+
+	/// called at the very end of loading to check for any missing ID's
+	void finalize();
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & registeredObjects;
+		h & state;
+	}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 42 - 0
lib/modding/ModIncompatibility.h

@@ -0,0 +1,42 @@
+/*
+ * ModIncompatibility.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class DLL_LINKAGE ModIncompatibility: public std::exception
+{
+public:
+	using StringPair = std::pair<const std::string, const std::string>;
+	using ModList = std::list<StringPair>;
+
+	ModIncompatibility(ModList && _missingMods):
+		missingMods(std::move(_missingMods))
+	{
+		std::ostringstream _ss;
+		for(const auto & m : missingMods)
+			_ss << m.first << ' ' << m.second << std::endl;
+		message = _ss.str();
+	}
+
+	const char * what() const noexcept override
+	{
+		return message.c_str();
+	}
+
+private:
+	//list of mods required to load the game
+	// first: mod name
+	// second: mod version
+	const ModList missingMods;
+	std::string message;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 53 - 0
lib/modding/ModScope.h

@@ -0,0 +1,53 @@
+/*
+ * ModScope.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+namespace ModScope
+{
+
+/// returns true if scope is reserved for internal use and can not be used by mods
+inline bool isScopeReserved(const std::string & scope)
+{
+	//following scopes are reserved - either in use by mod system or by filesystem
+	static const std::array<std::string, 9> reservedScopes = {
+		"core", "map", "game", "root", "saves", "config", "local", "initial", "mapEditor"
+	};
+
+	return std::find(reservedScopes.begin(), reservedScopes.end(), scope) != reservedScopes.end();
+}
+
+/// reserved scope name for referencing built-in (e.g. H3) objects
+inline const std::string & scopeBuiltin()
+{
+	static const std::string scope = "core";
+	return scope;
+}
+
+/// reserved scope name for accessing objects from any loaded mod
+inline const std::string & scopeGame()
+{
+	static const std::string scope = "game";
+	return scope;
+}
+
+/// reserved scope name for accessing object for map loading
+inline const std::string & scopeMap()
+{
+	//TODO: implement accessing map dependencies for both H3 and VCMI maps
+	// for now, allow access to any identifiers
+	static const std::string scope = "game";
+	return scope;
+}
+
+};
+
+VCMI_LIB_NAMESPACE_END

+ 77 - 0
lib/modding/ModUtility.cpp

@@ -0,0 +1,77 @@
+/*
+ * CModHandler.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "ModUtility.h"
+
+#include <vstd/StringUtils.h>
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+std::string ModUtility::normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier)
+{
+	auto p = vstd::splitStringToPair(identifier, ':');
+
+	if(p.first.empty())
+		p.first = scope;
+
+	if(p.first == remoteScope)
+		p.first.clear();
+
+	return p.first.empty() ? p.second : p.first + ":" + p.second;
+}
+
+void ModUtility::parseIdentifier(const std::string & fullIdentifier, std::string & scope, std::string & type, std::string & identifier)
+{
+	auto p = vstd::splitStringToPair(fullIdentifier, ':');
+
+	scope = p.first;
+
+	auto p2 = vstd::splitStringToPair(p.second, '.');
+
+	if(!p2.first.empty())
+	{
+		type = p2.first;
+		identifier = p2.second;
+	}
+	else
+	{
+		type = p.second;
+		identifier.clear();
+	}
+}
+
+std::string ModUtility::makeFullIdentifier(const std::string & scope, const std::string & type, const std::string & identifier)
+{
+	if(type.empty())
+		logGlobal->error("Full identifier (%s %s) requires type name", scope, identifier);
+
+	std::string actualScope = scope;
+	std::string actualName = identifier;
+
+	//ignore scope if identifier is scoped
+	auto scopeAndName = vstd::splitStringToPair(identifier, ':');
+
+	if(!scopeAndName.first.empty())
+	{
+		actualScope = scopeAndName.first;
+		actualName = scopeAndName.second;
+	}
+
+	if(actualScope.empty())
+	{
+		return actualName.empty() ? type : type + "." + actualName;
+	}
+	else
+	{
+		return actualName.empty() ? actualScope+ ":" + type : actualScope + ":" + type + "." + actualName;
+	}
+}
+
+VCMI_LIB_NAMESPACE_END

+ 25 - 0
lib/modding/ModUtility.h

@@ -0,0 +1,25 @@
+/*
+ * ModUtility.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+// NOTE: all methods in this namespace should be considered internal to modding system and should not be used outside of this module
+namespace ModUtility
+{
+	DLL_LINKAGE std::string normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier);
+
+	DLL_LINKAGE void parseIdentifier(const std::string & fullIdentifier, std::string & scope, std::string & type, std::string & identifier);
+
+	DLL_LINKAGE std::string makeFullIdentifier(const std::string & scope, const std::string & type, const std::string & identifier);
+
+};
+
+VCMI_LIB_NAMESPACE_END

+ 0 - 1
lib/registerTypes/RegisterTypes.h

@@ -17,7 +17,6 @@
 #include "../CPlayerState.h"
 #include "../CHeroHandler.h"
 #include "../CTownHandler.h"
-#include "../CModHandler.h" //needed?
 #include "../mapObjectConstructors/CRewardableConstructor.h"
 #include "../mapObjectConstructors/CommonConstructors.h"
 #include "../mapObjectConstructors/CBankInstanceConstructor.h"

+ 0 - 1
lib/registerTypes/TypesClientPacks1.cpp

@@ -11,7 +11,6 @@
 #include "RegisterTypes.h"
 
 #include "../StartInfo.h"
-#include "../CModHandler.h"
 #include "../mapObjects/CObjectHandler.h"
 #include "../CCreatureHandler.h"
 #include "../VCMI_Lib.h"

+ 0 - 1
lib/registerTypes/TypesClientPacks2.cpp

@@ -13,7 +13,6 @@
 #include "../StartInfo.h"
 #include "../CStack.h"
 #include "../battle/BattleInfo.h"
-#include "../CModHandler.h"
 #include "../mapObjects/CObjectHandler.h"
 #include "../CCreatureHandler.h"
 #include "../VCMI_Lib.h"

+ 0 - 1
lib/registerTypes/TypesLobbyPacks.cpp

@@ -16,7 +16,6 @@
 #include "../gameState/CGameStateCampaign.h"
 #include "../gameState/TavernHeroesPool.h"
 #include "../mapping/CMap.h"
-#include "../CModHandler.h"
 #include "../mapObjects/CObjectHandler.h"
 #include "../CCreatureHandler.h"
 #include "../VCMI_Lib.h"

+ 0 - 1
lib/registerTypes/TypesMapObjects1.cpp

@@ -11,7 +11,6 @@
 #include "RegisterTypes.h"
 
 #include "../StartInfo.h"
-#include "../CModHandler.h"
 #include "../mapObjects/CObjectHandler.h"
 #include "../CCreatureHandler.h"
 #include "../VCMI_Lib.h"

+ 0 - 1
lib/registerTypes/TypesMapObjects2.cpp

@@ -13,7 +13,6 @@
 #include "../StartInfo.h"
 #include "../CStack.h"
 #include "../battle/BattleInfo.h"
-#include "../CModHandler.h"
 #include "../mapObjects/CObjectHandler.h"
 #include "../CCreatureHandler.h"
 #include "../VCMI_Lib.h"

+ 0 - 1
lib/registerTypes/TypesMapObjects3.cpp

@@ -11,7 +11,6 @@
 #include "RegisterTypes.h"
 
 #include "../StartInfo.h"
-#include "../CModHandler.h"
 #include "../mapObjects/CObjectHandler.h"
 #include "../CCreatureHandler.h"
 #include "../VCMI_Lib.h"

+ 0 - 1
lib/registerTypes/TypesServerPacks.cpp

@@ -11,7 +11,6 @@
 #include "RegisterTypes.h"
 
 #include "../StartInfo.h"
-#include "../CModHandler.h"
 #include "../mapObjects/CObjectHandler.h"
 #include "../CCreatureHandler.h"
 #include "../VCMI_Lib.h"

+ 3 - 3
lib/rewardable/Info.cpp

@@ -16,10 +16,10 @@
 #include "Reward.h"
 
 #include "../CGeneralTextHandler.h"
-#include "../CModHandler.h"
 #include "../IGameCallback.h"
 #include "../JsonRandom.h"
 #include "../mapObjects/IObjectInterface.h"
+#include "../modding/IdentifierStorage.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -165,8 +165,8 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRand
 
 	for ( auto node : source["changeCreatures"].Struct() )
 	{
-		CreatureID from(VLC->modh->identifiers.getIdentifier(node.second.meta, "creature", node.first).value());
-		CreatureID dest(VLC->modh->identifiers.getIdentifier(node.second.meta, "creature", node.second.String()).value());
+		CreatureID from(VLC->identifiers()->getIdentifier(node.second.meta, "creature", node.first).value());
+		CreatureID dest(VLC->identifiers()->getIdentifier(node.second.meta, "creature", node.second.String()).value());
 
 		reward.extraComponents.emplace_back(Component::EComponentType::CREATURE, dest.getNum(), 0, 0);
 

+ 2 - 2
lib/rmg/CRmgTemplate.cpp

@@ -16,9 +16,9 @@
 
 #include "../VCMI_Lib.h"
 #include "../CTownHandler.h"
-#include "../CModHandler.h"
 #include "../TerrainHandler.h"
 #include "../serializer/JsonSerializeFormat.h"
+#include "../modding/ModScope.h"
 #include "../StringConstants.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -71,7 +71,7 @@ class TerrainEncoder
 public:
 	static si32 decode(const std::string & identifier)
 	{
-		return *VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "terrain", identifier);
+		return *VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "terrain", identifier);
 	}
 
 	static std::string encode(const si32 index)

+ 0 - 1
lib/rmg/CRmgTemplateStorage.cpp

@@ -14,7 +14,6 @@
 #include "CRmgTemplate.h"
 
 #include "../serializer/JsonDeserializer.h"
-#include "../CModHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 3 - 2
lib/rmg/modificators/RoadPlacer.cpp

@@ -16,8 +16,9 @@
 #include "../Functions.h"
 #include "../CMapGenerator.h"
 #include "../threadpool/MapProxy.h"
-#include "../../CModHandler.h"
 #include "../../mapping/CMapEditManager.h"
+#include "../../modding/IdentifierStorage.h"
+#include "../../modding/ModScope.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -133,7 +134,7 @@ void RoadPlacer::drawRoads(bool secondary)
 	auto tiles = roads.getTilesVector();
 
 	std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType);
-	RoadId roadType(*VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "road", roadName));
+	RoadId roadType(*VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "road", roadName));
 
 	//If our road type is not enabled, choose highest below it
 	for (int8_t bestRoad = roadType.getNum(); bestRoad > RoadId(Road::NO_ROAD).getNum(); bestRoad--)

+ 2 - 2
lib/serializer/JsonSerializeFormat.h

@@ -10,7 +10,7 @@
 #pragma once
 
 #include "../JsonNode.h"
-#include "../CModHandler.h"
+#include "../modding/IdentifierStorage.h"
 #include "../VCMI_Lib.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -369,7 +369,7 @@ public:
 
 				for(const auto & id : node.Vector())
 				{
-					VLC->modh->identifiers.requestIdentifier(U::entityType(), id, [&value](int32_t identifier)
+					VLC->identifiers()->requestIdentifier(U::entityType(), id, [&value](int32_t identifier)
 					{
 						value.emplace(identifier);
 					});

+ 5 - 4
lib/spells/CSpellHandler.cpp

@@ -20,7 +20,6 @@
 #include "../CGeneralTextHandler.h"
 #include "../filesystem/Filesystem.h"
 
-#include "../CModHandler.h"
 #include "../StringConstants.h"
 
 #include "../battle/BattleInfo.h"
@@ -29,6 +28,8 @@
 
 #include "../mapObjects/CGHeroInstance.h" //todo: remove
 #include "../serializer/CSerializer.h"
+#include "../modding/IdentifierStorage.h"
+#include "../modding/ModUtility.h"
 
 #include "ISpellMechanics.h"
 
@@ -458,7 +459,7 @@ JsonNode CSpell::convertTargetCondition(const BTVector & immunity, const BTVecto
 			auto iter = bonusNameRMap.find(bonusType);
 			if(iter != bonusNameRMap.end())
 			{
-				auto fullId = CModHandler::makeFullIdentifier("", "bonus", iter->second);
+				auto fullId = ModUtility::makeFullIdentifier("", "bonus", iter->second);
 				res[targetName][fullId].String() = value;
 			}
 			else
@@ -713,7 +714,7 @@ CSpell * CSpellHandler::loadFromJson(const std::string & scope, const JsonNode &
 	{
 		const int chance = static_cast<int>(node.second.Integer());
 
-		VLC->modh->identifiers.requestIdentifier(node.second.meta, "faction", node.first, [=](si32 factionID)
+		VLC->identifiers()->requestIdentifier(node.second.meta, "faction", node.first, [=](si32 factionID)
 		{
 			spell->probabilities[FactionID(factionID)] = chance;
 		});
@@ -736,7 +737,7 @@ CSpell * CSpellHandler::loadFromJson(const std::string & scope, const JsonNode &
 	{
 		if(counteredSpell.second.Bool())
 		{
-			VLC->modh->identifiers.requestIdentifier(counteredSpell.second.meta, counteredSpell.first, [=](si32 id) 
+			VLC->identifiers()->requestIdentifier(counteredSpell.second.meta, counteredSpell.first, [=](si32 id) 
 			{
 				spell->counteredSpells.emplace_back(id);
 			});

+ 5 - 4
lib/spells/TargetCondition.cpp

@@ -18,9 +18,10 @@
 #include "../bonuses/BonusParams.h"
 #include "../bonuses/BonusList.h"
 
+#include "../modding/IdentifierStorage.h"
+#include "../modding/ModUtility.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../VCMI_Lib.h"
-#include "../CModHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -371,7 +372,7 @@ public:
 		}
 		else if(type == "creature")
 		{
-			auto rawId = VLC->modh->identifiers.getIdentifier(scope, type, identifier, true);
+			auto rawId = VLC->identifiers()->getIdentifier(scope, type, identifier, true);
 
 			if(rawId)
 				return std::make_shared<CreatureCondition>(CreatureID(rawId.value()));
@@ -380,7 +381,7 @@ public:
 		}
 		else if(type == "spell")
 		{
-			auto rawId = VLC->modh->identifiers.getIdentifier(scope, type, identifier, true);
+			auto rawId = VLC->identifiers()->getIdentifier(scope, type, identifier, true);
 
 			if(rawId)
 				return std::make_shared<SpellEffectCondition>(SpellID(rawId.value()));
@@ -539,7 +540,7 @@ void TargetCondition::loadConditions(const JsonNode & source, bool exclusive, bo
 			std::string type;
 			std::string identifier;
 
-			CModHandler::parseIdentifier(keyValue.first, scope, type, identifier);
+			ModUtility::parseIdentifier(keyValue.first, scope, type, identifier);
 
 			item = itemFactory->createConfigurable(keyValue.second.meta, type, identifier);
 		}

+ 0 - 1
mapeditor/graphics.cpp

@@ -24,7 +24,6 @@
 #include "../lib/filesystem/CBinaryReader.h"
 #include "Animation.h"
 #include "../lib/CThreadHelper.h"
-#include "../lib/CModHandler.h"
 #include "../lib/VCMI_Lib.h"
 #include "../CCallback.h"
 #include "../lib/CGeneralTextHandler.h"

+ 0 - 1
mapeditor/inspector/townbulidingswidget.cpp

@@ -10,7 +10,6 @@
 #include "StdInc.h"
 #include "townbulidingswidget.h"
 #include "ui_townbulidingswidget.h"
-#include "../lib/CModHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 
 std::string defaultBuildingIdConversion(BuildingID bId)

+ 4 - 4
mapeditor/mainwindow.cpp

@@ -21,7 +21,6 @@
 #include "../lib/VCMI_Lib.h"
 #include "../lib/logging/CBasicLogConfigurator.h"
 #include "../lib/CConfigHandler.h"
-#include "../lib/CModHandler.h"
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/GameConstants.h"
 #include "../lib/mapObjectConstructors/AObjectTypeHandler.h"
@@ -31,6 +30,7 @@
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapping/CMapEditManager.h"
 #include "../lib/mapping/MapFormat.h"
+#include "../lib/modding/ModIncompatibility.h"
 #include "../lib/RoadHandler.h"
 #include "../lib/RiverHandler.h"
 #include "../lib/TerrainHandler.h"
@@ -336,17 +336,17 @@ bool MainWindow::openMap(const QString & filenameSelect)
 		if(auto header = mapService.loadMapHeader(resId))
 		{
 			auto missingMods = CMapService::verifyMapHeaderMods(*header);
-			CModHandler::Incompatibility::ModList modList;
+			ModIncompatibility::ModList modList;
 			for(const auto & m : missingMods)
 				modList.push_back({m.first, m.second.toString()});
 			
 			if(!modList.empty())
-				throw CModHandler::Incompatibility(std::move(modList));
+				throw ModIncompatibility(std::move(modList));
 			
 			controller.setMap(mapService.loadMap(resId));
 		}
 	}
-	catch(const CModHandler::Incompatibility & e)
+	catch(const ModIncompatibility & e)
 	{
 		QMessageBox::warning(this, "Mods requiered", e.what());
 		return false;

+ 2 - 1
mapeditor/mapcontroller.cpp

@@ -19,11 +19,12 @@
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapping/CMapEditManager.h"
 #include "../lib/mapping/ObstacleProxy.h"
+#include "../lib/modding/CModHandler.h"
+#include "../lib/modding/CModInfo.h"
 #include "../lib/TerrainHandler.h"
 #include "../lib/CSkillHandler.h"
 #include "../lib/spells/CSpellHandler.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CModHandler.h"
 #include "../lib/serializer/CMemorySerializer.h"
 #include "mapview.h"
 #include "scenelayer.h"

+ 1 - 1
mapeditor/mapcontroller.h

@@ -13,7 +13,7 @@
 #include "maphandler.h"
 #include "mapview.h"
 
-#include "../lib/CModVersion.h"
+#include "../lib/modding/CModVersion.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 using ModCompatibilityInfo = std::map<std::string, CModVersion>;

+ 0 - 1
mapeditor/maphandler.cpp

@@ -20,7 +20,6 @@
 #include "../lib/mapObjects/ObjectTemplate.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/CTownHandler.h"
-#include "../lib/CModHandler.h"
 #include "../lib/GameConstants.h"
 #include "../lib/JsonDetail.h"
 

+ 2 - 1
mapeditor/mapsettings.cpp

@@ -17,10 +17,11 @@
 #include "../lib/CArtHandler.h"
 #include "../lib/CHeroHandler.h"
 #include "../lib/CGeneralTextHandler.h"
-#include "../lib/CModHandler.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/mapObjects/CGCreature.h"
 #include "../lib/mapping/CMapService.h"
+#include "../lib/modding/CModHandler.h"
+#include "../lib/modding/CModInfo.h"
 #include "../lib/StringConstants.h"
 #include "inspector/townbulidingswidget.h" //to convert BuildingID to string
 

+ 2 - 1
mapeditor/validator.cpp

@@ -14,8 +14,9 @@
 #include "ui_validator.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapObjects/MapObjects.h"
+#include "../lib/modding/CModHandler.h"
+#include "../lib/modding/CModInfo.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CModHandler.h"
 
 Validator::Validator(const CMap * map, QWidget *parent) :
 	QDialog(parent),

+ 3 - 3
scripting/lua/LuaScriptingContext.cpp

@@ -24,7 +24,7 @@
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/battle/IBattleInfoCallback.h"
 #include "../../lib/CGameInfoCallback.h"
-#include "../../lib/CModHandler.h"
+#include "../../lib/modding/ModScope.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -511,14 +511,14 @@ int LuaContext::loadModule()
 
 		registar->pushMetatable(L);
 	}
-	else if(scope == CModHandler::scopeBuiltin())
+	else if(scope == ModScope::scopeBuiltin())
 	{
 
 	//	boost::algorithm::replace_all(modulePath, boost::is_any_of("\\/ "), "");
 
 		boost::algorithm::replace_all(modulePath, ".", "/");
 
-		auto *loader = CResourceHandler::get(CModHandler::scopeBuiltin());
+		auto *loader = CResourceHandler::get(ModScope::scopeBuiltin());
 
 		modulePath = "scripts/lib/" + modulePath;
 

+ 3 - 3
server/CGameHandler.cpp

@@ -22,7 +22,6 @@
 #include "../lib/int3.h"
 #include "../lib/ArtifactUtils.h"
 #include "../lib/StartInfo.h"
-#include "../lib/CModHandler.h"
 #include "../lib/CArtHandler.h"
 #include "../lib/CBuildingHandler.h"
 #include "../lib/CHeroHandler.h"
@@ -46,6 +45,7 @@
 #include "../lib/VCMI_Lib.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapping/CMapService.h"
+#include "../lib/modding/ModIncompatibility.h"
 #include "../lib/rmg/CMapGenOptions.h"
 #include "../lib/VCMIDirs.h"
 #include "../lib/ScopeGuard.h"
@@ -2113,7 +2113,7 @@ void CGameHandler::setupBattle(int3 tile, const CArmedInstance *armies[2], const
 
 	BattleField terType = gs->battleGetBattlefieldType(tile, getRandomGenerator());
 	if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat)
-		terType = BattleField(*VLC->modh->identifiers.getIdentifier("core", "battlefield", "ship_to_ship"));
+		terType = BattleField(*VLC->identifiers()->getIdentifier("core", "battlefield", "ship_to_ship"));
 
 	//send info about battles
 	BattleStart bs;
@@ -2936,7 +2936,7 @@ bool CGameHandler::load(const std::string & filename)
 		}
 		logGlobal->info("Game has been successfully loaded!");
 	}
-	catch(const CModHandler::Incompatibility & e)
+	catch(const ModIncompatibility & e)
 	{
 		logGlobal->error("Failed to load game: %s", e.what());
 		auto errorMsg = VLC->generaltexth->translate("vcmi.server.errors.modsIncompatibility") + '\n';

+ 0 - 1
server/CVCMIServer.cpp

@@ -14,7 +14,6 @@
 #include "../lib/campaign/CampaignState.h"
 #include "../lib/CThreadHelper.h"
 #include "../lib/serializer/Connection.h"
-#include "../lib/CModHandler.h"
 #include "../lib/CArtHandler.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"

+ 3 - 2
server/PlayerMessageProcessor.cpp

@@ -16,13 +16,14 @@
 #include "../lib/serializer/Connection.h"
 #include "../lib/CGeneralTextHandler.h"
 #include "../lib/CHeroHandler.h"
-#include "../lib/CModHandler.h"
 #include "../lib/CPlayerState.h"
 #include "../lib/GameConstants.h"
 #include "../lib/NetPacks.h"
 #include "../lib/StartInfo.h"
 #include "../lib/gameState/CGameState.h"
 #include "../lib/mapObjects/CGTownInstance.h"
+#include "../lib/modding/IdentifierStorage.h"
+#include "../lib/modding/ModScope.h"
 
 PlayerMessageProcessor::PlayerMessageProcessor()
 	:gameHandler(nullptr)
@@ -179,7 +180,7 @@ void PlayerMessageProcessor::cheatGiveArmy(PlayerColor player, const CGHeroInsta
 	{
 	}
 
-	std::optional<int32_t> creatureId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "creature", creatureIdentifier, false);
+	std::optional<int32_t> creatureId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "creature", creatureIdentifier, false);
 
 	if(creatureId.has_value())
 	{

+ 2 - 2
test/scripting/ScriptFixture.cpp

@@ -8,7 +8,7 @@
  *
  */
 #include "StdInc.h"
-#include "lib/CModHandler.h"
+#include "lib/modding/ModScope.h"
 
 #include "ScriptFixture.h"
 
@@ -30,7 +30,7 @@ void ScriptFixture::loadScriptFromFile(const std::string & path)
 
 void ScriptFixture::loadScript(const JsonNode & scriptConfig)
 {
-	subject = VLC->scriptHandler->loadFromJson(&loggerMock, CModHandler::scopeBuiltin(), scriptConfig, "test");
+	subject = VLC->scriptHandler->loadFromJson(&loggerMock, ModScope::scopeBuiltin(), scriptConfig, "test");
 
 	GTEST_ASSERT_NE(subject, nullptr);
 

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels