CModHandler.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /*
  2. * CModHandler.h, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #pragma once
  11. #include "JsonNode.h"
  12. #include "CModVersion.h"
  13. #ifdef __UCLIBC__
  14. #undef major
  15. #undef minor
  16. #undef patch
  17. #endif
  18. VCMI_LIB_NAMESPACE_BEGIN
  19. class CModHandler;
  20. class CModIndentifier;
  21. class CModInfo;
  22. class JsonNode;
  23. class IHandlerBase;
  24. /// class that stores all object identifiers strings and maps them to numeric ID's
  25. /// if possible, objects ID's should be in format <type>.<name>, camelCase e.g. "creature.grandElf"
  26. class DLL_LINKAGE CIdentifierStorage
  27. {
  28. enum ELoadingState
  29. {
  30. LOADING,
  31. FINALIZING,
  32. FINISHED
  33. };
  34. struct ObjectCallback // entry created on ID request
  35. {
  36. std::string localScope; /// scope from which this ID was requested
  37. std::string remoteScope; /// scope in which this object must be found
  38. std::string type; /// type, e.g. creature, faction, hero, etc
  39. std::string name; /// string ID
  40. std::function<void(si32)> callback;
  41. bool optional;
  42. /// Builds callback from identifier in form "targetMod:type.name"
  43. static ObjectCallback fromNameWithType(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback, bool optional);
  44. /// Builds callback from identifier in form "targetMod:name"
  45. static ObjectCallback fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional);
  46. private:
  47. ObjectCallback() = default;
  48. };
  49. struct ObjectData // entry created on ID registration
  50. {
  51. si32 id;
  52. std::string scope; /// scope in which this ID located
  53. bool operator==(const ObjectData & other) const
  54. {
  55. return id == other.id && scope == other.scope;
  56. }
  57. template <typename Handler> void serialize(Handler &h, const int version)
  58. {
  59. h & id;
  60. h & scope;
  61. }
  62. };
  63. std::multimap<std::string, ObjectData> registeredObjects;
  64. std::vector<ObjectCallback> scheduledRequests;
  65. ELoadingState state;
  66. /// Check if identifier can be valid (camelCase, point as separator)
  67. static void checkIdentifier(std::string & ID);
  68. void requestIdentifier(ObjectCallback callback);
  69. bool resolveIdentifier(const ObjectCallback & callback);
  70. std::vector<ObjectData> getPossibleIdentifiers(const ObjectCallback & callback);
  71. public:
  72. CIdentifierStorage();
  73. virtual ~CIdentifierStorage() = default;
  74. /// request identifier for specific object name.
  75. /// Function callback will be called during ID resolution phase of loading
  76. void requestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback);
  77. ///fullName = [remoteScope:]type.name
  78. void requestIdentifier(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback);
  79. void requestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback);
  80. void requestIdentifier(const JsonNode & name, const std::function<void(si32)> & callback);
  81. /// try to request ID. If ID with such name won't be loaded, callback function will not be called
  82. void tryRequestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback);
  83. void tryRequestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback);
  84. /// get identifier immediately. If identifier is not know and not silent call will result in error message
  85. std::optional<si32> getIdentifier(const std::string & scope, const std::string & type, const std::string & name, bool silent = false);
  86. std::optional<si32> getIdentifier(const std::string & type, const JsonNode & name, bool silent = false);
  87. std::optional<si32> getIdentifier(const JsonNode & name, bool silent = false);
  88. std::optional<si32> getIdentifier(const std::string & scope, const std::string & fullName, bool silent = false);
  89. /// registers new object
  90. void registerObject(const std::string & scope, const std::string & type, const std::string & name, si32 identifier);
  91. /// called at the very end of loading to check for any missing ID's
  92. void finalize();
  93. template <typename Handler> void serialize(Handler &h, const int version)
  94. {
  95. h & registeredObjects;
  96. h & state;
  97. }
  98. };
  99. /// internal type to handle loading of one data type (e.g. artifacts, creatures)
  100. class DLL_LINKAGE ContentTypeHandler
  101. {
  102. public:
  103. struct ModInfo
  104. {
  105. /// mod data from this mod and for this mod
  106. JsonNode modData;
  107. /// mod data for this mod from other mods (patches)
  108. JsonNode patches;
  109. };
  110. /// handler to which all data will be loaded
  111. IHandlerBase * handler;
  112. std::string objectName;
  113. /// contains all loaded H3 data
  114. std::vector<JsonNode> originalData;
  115. std::map<std::string, ModInfo> modData;
  116. ContentTypeHandler(IHandlerBase * handler, const std::string & objectName);
  117. /// local version of methods in ContentHandler
  118. /// returns true if loading was successful
  119. bool preloadModData(const std::string & modName, const std::vector<std::string> & fileList, bool validate);
  120. bool loadMod(const std::string & modName, bool validate);
  121. void loadCustom();
  122. void afterLoadFinalization();
  123. };
  124. /// class used to load all game data into handlers. Used only during loading
  125. class DLL_LINKAGE CContentHandler
  126. {
  127. /// preloads all data from fileList as data from modName.
  128. bool preloadModData(const std::string & modName, JsonNode modConfig, bool validate);
  129. /// actually loads data in mod
  130. bool loadMod(const std::string & modName, bool validate);
  131. std::map<std::string, ContentTypeHandler> handlers;
  132. public:
  133. void init();
  134. /// preloads all data from fileList as data from modName.
  135. void preloadData(CModInfo & mod);
  136. /// actually loads data in mod
  137. void load(CModInfo & mod);
  138. void loadCustom();
  139. /// all data was loaded, time for final validation / integration
  140. void afterLoadFinalization();
  141. const ContentTypeHandler & operator[] (const std::string & name) const;
  142. };
  143. using TModID = std::string;
  144. class DLL_LINKAGE CModInfo
  145. {
  146. /// cached result of checkModGameplayAffecting() call
  147. /// Do not serialize - depends on local mod version, not server/save mod version
  148. mutable std::optional<bool> modGameplayAffecting;
  149. public:
  150. enum EValidationStatus
  151. {
  152. PENDING,
  153. FAILED,
  154. PASSED
  155. };
  156. /// identifier, identical to name of folder with mod
  157. std::string identifier;
  158. /// human-readable strings
  159. std::string name;
  160. std::string description;
  161. /// version of the mod
  162. CModVersion version;
  163. /// Base language of mod, all mod strings are assumed to be in this language
  164. std::string baseLanguage;
  165. /// vcmi versions compatible with the mod
  166. CModVersion vcmiCompatibleMin, vcmiCompatibleMax;
  167. /// list of mods that should be loaded before this one
  168. std::set <TModID> dependencies;
  169. /// list of mods that can't be used in the same time as this one
  170. std::set <TModID> conflicts;
  171. /// CRC-32 checksum of the mod
  172. ui32 checksum;
  173. EValidationStatus validation;
  174. JsonNode config;
  175. CModInfo();
  176. CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config);
  177. JsonNode saveLocalData() const;
  178. void updateChecksum(ui32 newChecksum);
  179. /// return true if this mod can affect gameplay, e.g. adds or modifies any game objects
  180. bool checkModGameplayAffecting() const;
  181. bool isEnabled() const;
  182. void setEnabled(bool on);
  183. static std::string getModDir(const std::string & name);
  184. static std::string getModFile(const std::string & name);
  185. private:
  186. /// true if mod is enabled by user, e.g. in Launcher UI
  187. bool explicitlyEnabled;
  188. /// true if mod can be loaded - compatible and has no missing deps
  189. bool implicitlyEnabled;
  190. void loadLocalData(const JsonNode & data);
  191. };
  192. class DLL_LINKAGE CModHandler
  193. {
  194. std::map <TModID, CModInfo> allMods;
  195. std::vector <TModID> activeMods;//active mods, in order in which they were loaded
  196. CModInfo coreMod;
  197. bool hasCircularDependency(const TModID & mod, std::set<TModID> currentList = std::set<TModID>()) const;
  198. /**
  199. * 1. Set apart mods with resolved dependencies from mods which have unresolved dependencies
  200. * 2. Sort resolved mods using topological algorithm
  201. * 3. Log all problem mods and their unresolved dependencies
  202. *
  203. * @param modsToResolve list of valid mod IDs (checkDependencies returned true - TODO: Clarify it.)
  204. * @return a vector of the topologically sorted resolved mods: child nodes (dependent mods) have greater index than parents
  205. */
  206. std::vector <TModID> validateAndSortDependencies(std::vector <TModID> modsToResolve) const;
  207. std::vector<std::string> getModList(const std::string & path) const;
  208. void loadMods(const std::string & path, const std::string & parent, const JsonNode & modSettings, bool enableMods);
  209. void loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, bool enableMods);
  210. void loadTranslation(const TModID & modName);
  211. bool validateTranslations(TModID modName) const;
  212. public:
  213. /// returns true if scope is reserved for internal use and can not be used by mods
  214. static bool isScopeReserved(const TModID & scope);
  215. /// reserved scope name for referencing built-in (e.g. H3) objects
  216. static const TModID & scopeBuiltin();
  217. /// reserved scope name for accessing objects from any loaded mod
  218. static const TModID & scopeGame();
  219. /// reserved scope name for accessing object for map loading
  220. static const TModID & scopeMap();
  221. class DLL_LINKAGE Incompatibility: public std::exception
  222. {
  223. public:
  224. using StringPair = std::pair<const std::string, const std::string>;
  225. using ModList = std::list<StringPair>;
  226. Incompatibility(ModList && _missingMods):
  227. missingMods(std::move(_missingMods))
  228. {
  229. std::ostringstream _ss;
  230. for(const auto & m : missingMods)
  231. _ss << m.first << ' ' << m.second << std::endl;
  232. message = _ss.str();
  233. }
  234. const char * what() const noexcept override
  235. {
  236. return message.c_str();
  237. }
  238. private:
  239. //list of mods required to load the game
  240. // first: mod name
  241. // second: mod version
  242. const ModList missingMods;
  243. std::string message;
  244. };
  245. CIdentifierStorage identifiers;
  246. std::shared_ptr<CContentHandler> content; //(!)Do not serialize
  247. /// receives list of available mods and trying to load mod.json from all of them
  248. void initializeConfig();
  249. void loadMods(bool onlyEssential = false);
  250. void loadModFilesystems();
  251. /// returns ID of mod that provides selected file resource
  252. TModID findResourceOrigin(const ResourceID & name);
  253. std::string getModLanguage(const TModID & modId) const;
  254. std::set<TModID> getModDependencies(const TModID & modId, bool & isModFound) const;
  255. /// returns list of all (active) mods
  256. std::vector<std::string> getAllMods();
  257. std::vector<std::string> getActiveMods();
  258. const CModInfo & getModInfo(const TModID & modId) const;
  259. /// load content from all available mods
  260. void load();
  261. void afterLoad(bool onlyEssential);
  262. CModHandler();
  263. virtual ~CModHandler() = default;
  264. static std::string normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier);
  265. static void parseIdentifier(const std::string & fullIdentifier, std::string & scope, std::string & type, std::string & identifier);
  266. static std::string makeFullIdentifier(const std::string & scope, const std::string & type, const std::string & identifier);
  267. template <typename Handler> void serialize(Handler &h, const int version)
  268. {
  269. if(h.saving)
  270. {
  271. h & activeMods;
  272. for(const auto & m : activeMods)
  273. h & allMods[m].version;
  274. }
  275. else
  276. {
  277. loadMods();
  278. std::vector<TModID> saveActiveMods;
  279. std::vector<TModID> newActiveMods;
  280. h & saveActiveMods;
  281. Incompatibility::ModList missingMods;
  282. for(const auto & m : activeMods)
  283. {
  284. if (vstd::contains(saveActiveMods, m))
  285. continue;
  286. auto & modInfo = allMods.at(m);
  287. if(modInfo.checkModGameplayAffecting())
  288. missingMods.emplace_back(m, modInfo.version.toString());
  289. }
  290. for(const auto & m : saveActiveMods)
  291. {
  292. CModVersion mver;
  293. h & mver;
  294. if (allMods.count(m) == 0)
  295. {
  296. missingMods.emplace_back(m, mver.toString());
  297. continue;
  298. }
  299. auto & modInfo = allMods.at(m);
  300. bool modAffectsGameplay = modInfo.checkModGameplayAffecting();
  301. bool modVersionCompatible = modInfo.version.isNull() || mver.isNull() || modInfo.version.compatible(mver);
  302. bool modEnabledLocally = vstd::contains(activeMods, m);
  303. bool modCanBeEnabled = modEnabledLocally && modVersionCompatible;
  304. allMods[m].setEnabled(modCanBeEnabled);
  305. if (modCanBeEnabled)
  306. newActiveMods.push_back(m);
  307. if (!modCanBeEnabled && modAffectsGameplay)
  308. missingMods.emplace_back(m, mver.toString());
  309. }
  310. if(!missingMods.empty())
  311. throw Incompatibility(std::move(missingMods));
  312. std::swap(activeMods, newActiveMods);
  313. }
  314. h & identifiers;
  315. }
  316. };
  317. VCMI_LIB_NAMESPACE_END