CModHandler.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  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. public:
  147. enum EValidationStatus
  148. {
  149. PENDING,
  150. FAILED,
  151. PASSED
  152. };
  153. /// identifier, identical to name of folder with mod
  154. std::string identifier;
  155. /// human-readable strings
  156. std::string name;
  157. std::string description;
  158. /// version of the mod
  159. CModVersion version;
  160. /// Base language of mod, all mod strings are assumed to be in this language
  161. std::string baseLanguage;
  162. /// vcmi versions compatible with the mod
  163. CModVersion vcmiCompatibleMin, vcmiCompatibleMax;
  164. /// list of mods that should be loaded before this one
  165. std::set <TModID> dependencies;
  166. /// list of mods that can't be used in the same time as this one
  167. std::set <TModID> conflicts;
  168. /// CRC-32 checksum of the mod
  169. ui32 checksum;
  170. EValidationStatus validation;
  171. JsonNode config;
  172. CModInfo();
  173. CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config);
  174. JsonNode saveLocalData() const;
  175. void updateChecksum(ui32 newChecksum);
  176. bool isEnabled() const;
  177. void setEnabled(bool on);
  178. static std::string getModDir(const std::string & name);
  179. static std::string getModFile(const std::string & name);
  180. private:
  181. /// true if mod is enabled by user, e.g. in Launcher UI
  182. bool explicitlyEnabled;
  183. /// true if mod can be loaded - compatible and has no missing deps
  184. bool implicitlyEnabled;
  185. void loadLocalData(const JsonNode & data);
  186. };
  187. class DLL_LINKAGE CModHandler
  188. {
  189. std::map <TModID, CModInfo> allMods;
  190. std::vector <TModID> activeMods;//active mods, in order in which they were loaded
  191. CModInfo coreMod;
  192. bool hasCircularDependency(const TModID & mod, std::set<TModID> currentList = std::set<TModID>()) const;
  193. /**
  194. * 1. Set apart mods with resolved dependencies from mods which have unresolved dependencies
  195. * 2. Sort resolved mods using topological algorithm
  196. * 3. Log all problem mods and their unresolved dependencies
  197. *
  198. * @param modsToResolve list of valid mod IDs (checkDependencies returned true - TODO: Clarify it.)
  199. * @return a vector of the topologically sorted resolved mods: child nodes (dependent mods) have greater index than parents
  200. */
  201. std::vector <TModID> validateAndSortDependencies(std::vector <TModID> modsToResolve) const;
  202. std::vector<std::string> getModList(const std::string & path) const;
  203. void loadMods(const std::string & path, const std::string & parent, const JsonNode & modSettings, bool enableMods);
  204. void loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, bool enableMods);
  205. void loadTranslation(const TModID & modName);
  206. bool validateTranslations(TModID modName) const;
  207. public:
  208. /// returns true if scope is reserved for internal use and can not be used by mods
  209. static bool isScopeReserved(const TModID & scope);
  210. /// reserved scope name for referencing built-in (e.g. H3) objects
  211. static const TModID & scopeBuiltin();
  212. /// reserved scope name for accessing objects from any loaded mod
  213. static const TModID & scopeGame();
  214. /// reserved scope name for accessing object for map loading
  215. static const TModID & scopeMap();
  216. class DLL_LINKAGE Incompatibility: public std::exception
  217. {
  218. public:
  219. using StringPair = std::pair<const std::string, const std::string>;
  220. using ModList = std::list<StringPair>;
  221. Incompatibility(ModList && _missingMods):
  222. missingMods(std::move(_missingMods))
  223. {
  224. std::ostringstream _ss;
  225. for(const auto & m : missingMods)
  226. _ss << m.first << ' ' << m.second << std::endl;
  227. message = _ss.str();
  228. }
  229. const char * what() const noexcept override
  230. {
  231. return message.c_str();
  232. }
  233. private:
  234. //list of mods required to load the game
  235. // first: mod name
  236. // second: mod version
  237. const ModList missingMods;
  238. std::string message;
  239. };
  240. CIdentifierStorage identifiers;
  241. std::shared_ptr<CContentHandler> content; //(!)Do not serialize
  242. /// receives list of available mods and trying to load mod.json from all of them
  243. void initializeConfig();
  244. void loadMods(bool onlyEssential = false);
  245. void loadModFilesystems();
  246. /// returns ID of mod that provides selected file resource
  247. TModID findResourceOrigin(const ResourceID & name);
  248. std::string getModLanguage(const TModID & modId) const;
  249. std::set<TModID> getModDependencies(const TModID & modId, bool & isModFound) const;
  250. /// returns list of all (active) mods
  251. std::vector<std::string> getAllMods();
  252. std::vector<std::string> getActiveMods();
  253. const CModInfo & getModInfo(const TModID & modId) const;
  254. /// load content from all available mods
  255. void load();
  256. void afterLoad(bool onlyEssential);
  257. CModHandler();
  258. virtual ~CModHandler() = default;
  259. static std::string normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier);
  260. static void parseIdentifier(const std::string & fullIdentifier, std::string & scope, std::string & type, std::string & identifier);
  261. static std::string makeFullIdentifier(const std::string & scope, const std::string & type, const std::string & identifier);
  262. template <typename Handler> void serialize(Handler &h, const int version)
  263. {
  264. if(h.saving)
  265. {
  266. h & activeMods;
  267. for(const auto & m : activeMods)
  268. h & allMods[m].version;
  269. }
  270. else
  271. {
  272. loadMods();
  273. std::vector<TModID> newActiveMods;
  274. h & newActiveMods;
  275. Incompatibility::ModList missingMods;
  276. for(const auto & m : newActiveMods)
  277. {
  278. CModVersion mver;
  279. h & mver;
  280. if(allMods.count(m) && (allMods[m].version.isNull() || mver.isNull() || allMods[m].version.compatible(mver)))
  281. allMods[m].setEnabled(true);
  282. else
  283. missingMods.emplace_back(m, mver.toString());
  284. }
  285. if(!missingMods.empty())
  286. throw Incompatibility(std::move(missingMods));
  287. std::swap(activeMods, newActiveMods);
  288. }
  289. h & identifiers;
  290. }
  291. };
  292. VCMI_LIB_NAMESPACE_END