1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3 #include "AILibraryManager.h"
4
5 #include "Interface/aidefines.h"
6 #include "Interface/SAIInterfaceLibrary.h"
7 #include "AIInterfaceLibraryInfo.h"
8 #include "AIInterfaceLibrary.h"
9 #include "SkirmishAILibraryInfo.h"
10 #include "SkirmishAIData.h"
11
12 #include "System/Util.h"
13 #include "System/Log/ILog.h"
14 #include "System/Platform/errorhandler.h"
15 #include "System/Platform/SharedLib.h"
16 #include "System/FileSystem/FileHandler.h"
17 #include "System/FileSystem/DataDirsAccess.h"
18 #include "System/FileSystem/FileQueryFlags.h"
19 #include "System/FileSystem/FileSystem.h"
20 #include "Sim/Misc/GlobalConstants.h"
21 #include "Sim/Misc/Team.h"
22 #include "Sim/Misc/TeamHandler.h"
23
24 #include <string>
25 #include <set>
26 #include <sstream>
27 #include <limits.h>
28
29
CAILibraryManager()30 CAILibraryManager::CAILibraryManager() {
31
32 ClearAllInfos();
33 GatherInterfaceLibrariesInfos();
34 GatherSkirmishAIsLibrariesInfos();
35 }
36
GatherInterfaceLibrariesInfos()37 void CAILibraryManager::GatherInterfaceLibrariesInfos() {
38
39 typedef std::vector<std::string> T_dirs;
40
41 // cause we use CFileHandler for searching files,
42 // we are automatically searching in all data-dirs
43
44 // Read from AI Interface info files
45 // we are looking for:
46 // {AI_INTERFACES_DATA_DIR}/{*}/{*}/InterfaceInfo.lua
47 T_dirs aiInterfaceDataDirs =
48 dataDirsAccess.FindDirsInDirectSubDirs(AI_INTERFACES_DATA_DIR);
49 typedef std::map<const AIInterfaceKey, std::set<std::string> > T_dupInt;
50 T_dupInt duplicateInterfaceInfoCheck;
51 for (T_dirs::iterator dir = aiInterfaceDataDirs.begin();
52 dir != aiInterfaceDataDirs.end(); ++dir) {
53 const std::string& possibleDataDir = *dir;
54 T_dirs infoFiles =
55 CFileHandler::FindFiles(possibleDataDir, "InterfaceInfo.lua");
56 if (!infoFiles.empty()) { // interface info is available
57 const std::string& infoFile = infoFiles.at(0);
58
59 // generate and store the interface info
60 CAIInterfaceLibraryInfo* interfaceInfo =
61 new CAIInterfaceLibraryInfo(infoFile);
62
63 interfaceInfo->SetDataDir(FileSystem::EnsureNoPathSepAtEnd(possibleDataDir));
64 interfaceInfo->SetDataDirCommon(
65 FileSystem::GetParent(possibleDataDir) + "common");
66
67 AIInterfaceKey interfaceKey = interfaceInfo->GetKey();
68
69 interfaceKeys.insert(interfaceKey);
70 if (interfaceInfos.find(interfaceKey) == interfaceInfos.end()) {
71 // no interface info with this key yet -> store it
72 interfaceInfos[interfaceKey] = interfaceInfo;
73 } else {
74 // duplicate interface info -> free
75 delete interfaceInfo;
76 interfaceInfo = NULL;
77 }
78
79 // for debug-info, in case one interface is specified multiple times
80 duplicateInterfaceInfoCheck[interfaceKey].insert(infoFile);
81 }
82 }
83
84 // filter out interfaces that are specified multiple times
85 for (T_dupInt::const_iterator info = duplicateInterfaceInfoCheck.begin();
86 info != duplicateInterfaceInfoCheck.end(); ++info) {
87 if (info->second.size() >= 2) {
88 duplicateInterfaceInfos[info->first] = info->second;
89
90 if (LOG_IS_ENABLED(L_ERROR)) {
91 LOG_L(L_ERROR, "Duplicate AI Interface Info found:");
92 LOG_L(L_ERROR, "\tfor interface: %s %s",
93 info->first.GetShortName().c_str(),
94 info->first.GetVersion().c_str());
95 LOG_L(L_ERROR, "\tin files:");
96 const std::string* lastDir = NULL;
97 std::set<std::string>::const_iterator dir;
98 for (dir = info->second.begin(); dir != info->second.end(); ++dir) {
99 LOG_L(L_ERROR, "\t%s", dir->c_str());
100 lastDir = &(*dir);
101 }
102 LOG_L(L_ERROR, "\tusing: %s", lastDir->c_str());
103 }
104 }
105 }
106 }
107
GatherSkirmishAIsLibrariesInfos()108 void CAILibraryManager::GatherSkirmishAIsLibrariesInfos() {
109
110 T_dupSkirm duplicateSkirmishAIInfoCheck;
111 GatherSkirmishAIsLibrariesInfosFromLuaFiles(duplicateSkirmishAIInfoCheck);
112 GatherSkirmishAIsLibrariesInfosFromInterfaceLibrary(duplicateSkirmishAIInfoCheck);
113 FilterDuplicateSkirmishAILibrariesInfos(duplicateSkirmishAIInfoCheck);
114 }
115
StoreSkirmishAILibraryInfos(T_dupSkirm duplicateSkirmishAIInfoCheck,CSkirmishAILibraryInfo * skirmishAIInfo,const std::string & sourceDesc)116 void CAILibraryManager::StoreSkirmishAILibraryInfos(T_dupSkirm duplicateSkirmishAIInfoCheck, CSkirmishAILibraryInfo* skirmishAIInfo, const std::string& sourceDesc) {
117
118 skirmishAIInfo->SetLuaAI(false);
119
120 SkirmishAIKey aiKey = skirmishAIInfo->GetKey();
121 AIInterfaceKey interfaceKey =
122 FindFittingInterfaceSpecifier(
123 skirmishAIInfo->GetInterfaceShortName(),
124 skirmishAIInfo->GetInterfaceVersion(),
125 interfaceKeys);
126 if (!interfaceKey.IsUnspecified()) {
127 SkirmishAIKey skirmishAIKey = SkirmishAIKey(aiKey, interfaceKey);
128 skirmishAIKeys.insert(skirmishAIKey);
129 if (skirmishAIInfos.find(skirmishAIKey) == skirmishAIInfos.end()) {
130 // no AI info with this key yet -> store it
131 skirmishAIInfos[skirmishAIKey] = skirmishAIInfo;
132 } else {
133 // duplicate AI info -> free
134 delete skirmishAIInfo;
135 skirmishAIInfo = NULL;
136 }
137
138 // for debug-info, in case one AI is specified multiple times
139 duplicateSkirmishAIInfoCheck[skirmishAIKey].insert(sourceDesc);
140 } else {
141 LOG_L(L_ERROR,
142 "Required AI Interface for Skirmish AI %s %s not found.",
143 skirmishAIInfo->GetShortName().c_str(),
144 skirmishAIInfo->GetVersion().c_str());
145 delete skirmishAIInfo;
146 skirmishAIInfo = NULL;
147 }
148 }
149
GatherSkirmishAIsLibrariesInfosFromLuaFiles(T_dupSkirm duplicateSkirmishAIInfoCheck)150 void CAILibraryManager::GatherSkirmishAIsLibrariesInfosFromLuaFiles(T_dupSkirm duplicateSkirmishAIInfoCheck) {
151
152 typedef std::vector<std::string> T_dirs;
153
154 // Read from Skirmish AI info and option files
155 // we are looking for:
156 // {SKIRMISH_AI_DATA_DIR}/{*}/{*}/AIInfo.lua
157 // {SKIRMISH_AI_DATA_DIR}/{*}/{*}/AIOptions.lua
158 T_dirs skirmishAIDataDirs = dataDirsAccess.FindDirsInDirectSubDirs(SKIRMISH_AI_DATA_DIR);
159 for (T_dirs::iterator dir = skirmishAIDataDirs.begin();
160 dir != skirmishAIDataDirs.end(); ++dir)
161 {
162 const std::string& possibleDataDir = *dir;
163 T_dirs infoFiles = CFileHandler::FindFiles(possibleDataDir,
164 "AIInfo.lua");
165 if (!infoFiles.empty()) { // skirmish AI info is available
166 const std::string& infoFile = infoFiles.at(0);
167
168 std::string optionFileName = "";
169 T_dirs optionFile = CFileHandler::FindFiles(possibleDataDir,
170 "AIOptions.lua");
171 if (!optionFile.empty()) {
172 optionFileName = optionFile.at(0);
173 }
174 // generate and store the ai info
175 CSkirmishAILibraryInfo* skirmishAIInfo =
176 new CSkirmishAILibraryInfo(infoFile, optionFileName);
177
178 skirmishAIInfo->SetDataDir(
179 FileSystem::EnsureNoPathSepAtEnd(possibleDataDir));
180 skirmishAIInfo->SetDataDirCommon(
181 FileSystem::GetParent(possibleDataDir) + "common");
182
183 StoreSkirmishAILibraryInfos(duplicateSkirmishAIInfoCheck, skirmishAIInfo, infoFile);
184 }
185 }
186 }
187
GatherSkirmishAIsLibrariesInfosFromInterfaceLibrary(T_dupSkirm duplicateSkirmishAIInfoCheck)188 void CAILibraryManager::GatherSkirmishAIsLibrariesInfosFromInterfaceLibrary(T_dupSkirm duplicateSkirmishAIInfoCheck) {
189
190 const T_interfaceInfos& intInfs = GetInterfaceInfos();
191 T_interfaceInfos::const_iterator intInfIt;
192 for (intInfIt = intInfs.begin(); intInfIt != intInfs.end(); ++intInfIt) {
193 // only try to lookup Skirmish AI infos through the Interface library
194 // if it explicitly states support for this in InterfaceInfo.lua
195 if (intInfIt->second->IsLookupSupported()) {
196 const CAIInterfaceLibrary* intLib = FetchInterface(intInfIt->second->GetKey());
197
198 const int aiCount = intLib->GetSkirmishAICount();
199 for (int aii = 0; aii < aiCount; ++aii) {
200 const std::map<std::string, std::string> rawInfos = intLib->GetSkirmishAIInfos(aii);
201 const std::string rawLuaOptions = intLib->GetSkirmishAIOptions(aii);
202
203 // generate and store the ai info
204 CSkirmishAILibraryInfo* skirmishAIInfo =
205 new CSkirmishAILibraryInfo(rawInfos, rawLuaOptions);
206
207 // NOTE We do not set the data-dir(s) for interface looked-up
208 // AIs. This is the duty of the AI Interface plugin.
209
210 StoreSkirmishAILibraryInfos(duplicateSkirmishAIInfoCheck, skirmishAIInfo, intInfIt->first.ToString());
211 }
212 }
213 }
214 }
215
FilterDuplicateSkirmishAILibrariesInfos(T_dupSkirm duplicateSkirmishAIInfoCheck)216 void CAILibraryManager::FilterDuplicateSkirmishAILibrariesInfos(T_dupSkirm duplicateSkirmishAIInfoCheck) {
217
218 // filter out skirmish AIs that are specified multiple times
219 for (T_dupSkirm::const_iterator info = duplicateSkirmishAIInfoCheck.begin();
220 info != duplicateSkirmishAIInfoCheck.end(); ++info) {
221 if (info->second.size() >= 2) {
222 duplicateSkirmishAIInfos[info->first] = info->second;
223
224 if (LOG_IS_ENABLED(L_WARNING)) {
225 LOG_L(L_WARNING, "Duplicate Skirmish AI Info found:");
226 LOG_L(L_WARNING, "\tfor Skirmish AI: %s %s", info->first.GetShortName().c_str(),
227 info->first.GetVersion().c_str());
228 LOG_L(L_WARNING, "\tin files:");
229 const std::string* lastDir = NULL;
230 std::set<std::string>::const_iterator dir;
231 for (dir = info->second.begin(); dir != info->second.end(); ++dir) {
232 LOG_L(L_WARNING, "\t%s", dir->c_str());
233 lastDir = &(*dir);
234 }
235 LOG_L(L_WARNING, "\tusing: %s", lastDir->c_str());
236 }
237 }
238 }
239 }
240
ClearAllInfos()241 void CAILibraryManager::ClearAllInfos() {
242
243 IAILibraryManager::T_interfaceInfos::iterator iii;
244 for (iii = interfaceInfos.begin(); iii != interfaceInfos.end(); ++iii) {
245 delete iii->second;
246 iii->second = NULL;
247 }
248 interfaceInfos.clear();
249
250 IAILibraryManager::T_skirmishAIInfos::iterator sai;
251 for (sai = skirmishAIInfos.begin(); sai != skirmishAIInfos.end(); ++sai) {
252 delete sai->second;
253 sai->second = NULL;
254 }
255 skirmishAIInfos.clear();
256
257 interfaceKeys.clear();
258 skirmishAIKeys.clear();
259
260 duplicateInterfaceInfos.clear();
261 duplicateSkirmishAIInfos.clear();
262 }
263
~CAILibraryManager()264 CAILibraryManager::~CAILibraryManager() {
265
266 ReleaseEverything();
267 ClearAllInfos();
268 }
269
GetInterfaceKeys() const270 const IAILibraryManager::T_interfaceSpecs& CAILibraryManager::GetInterfaceKeys() const {
271 return interfaceKeys;
272 }
GetSkirmishAIKeys() const273 const IAILibraryManager::T_skirmishAIKeys& CAILibraryManager::GetSkirmishAIKeys() const {
274 return skirmishAIKeys;
275 }
276
GetInterfaceInfos() const277 const IAILibraryManager::T_interfaceInfos& CAILibraryManager::GetInterfaceInfos() const {
278 return interfaceInfos;
279 }
GetSkirmishAIInfos() const280 const IAILibraryManager::T_skirmishAIInfos& CAILibraryManager::GetSkirmishAIInfos() const {
281 return skirmishAIInfos;
282 }
283
GetDuplicateInterfaceInfos() const284 const IAILibraryManager::T_dupInt& CAILibraryManager::GetDuplicateInterfaceInfos() const {
285 return duplicateInterfaceInfos;
286 }
GetDuplicateSkirmishAIInfos() const287 const IAILibraryManager::T_dupSkirm& CAILibraryManager::GetDuplicateSkirmishAIInfos() const {
288 return duplicateSkirmishAIInfos;
289 }
290
291
FittingSkirmishAIKeys(const SkirmishAIKey & skirmishAIKey) const292 std::vector<SkirmishAIKey> CAILibraryManager::FittingSkirmishAIKeys(
293 const SkirmishAIKey& skirmishAIKey) const {
294
295 std::vector<SkirmishAIKey> applyingKeys;
296
297 if (skirmishAIKey.IsUnspecified()) {
298 return applyingKeys;
299 }
300
301 bool checkVersion = false;
302 if (skirmishAIKey.GetVersion() != "") {
303 checkVersion = true;
304 }
305
306 std::set<SkirmishAIKey>::const_iterator sasi;
307 for (sasi = skirmishAIKeys.begin(); sasi != skirmishAIKeys.end(); ++sasi) {
308
309 // check if the ai name fits
310 if (skirmishAIKey.GetShortName() != sasi->GetShortName()) {
311 continue;
312 }
313
314 // check if the ai version fits (if one is specified)
315 if (checkVersion && skirmishAIKey.GetVersion() != sasi->GetVersion()) {
316 continue;
317 }
318
319 // if the programm raches here, we know that this key fits
320 applyingKeys.push_back(*sasi);
321 }
322
323 return applyingKeys;
324 }
325
326
327
FetchSkirmishAILibrary(const SkirmishAIKey & skirmishAIKey)328 const CSkirmishAILibrary* CAILibraryManager::FetchSkirmishAILibrary(const SkirmishAIKey& skirmishAIKey) {
329
330 const CSkirmishAILibrary* aiLib = NULL;
331
332 T_skirmishAIInfos::const_iterator aiInfo = skirmishAIInfos.find(skirmishAIKey);
333 if (aiInfo == skirmishAIInfos.end()) {
334 LOG_L(L_ERROR,
335 "Unknown skirmish AI specified: %s %s",
336 skirmishAIKey.GetShortName().c_str(),
337 skirmishAIKey.GetVersion().c_str()
338 );
339 } else {
340 CAIInterfaceLibrary* interfaceLib = FetchInterface(skirmishAIKey.GetInterface());
341 if ((interfaceLib != NULL) && interfaceLib->IsInitialized()) {
342 aiLib = interfaceLib->FetchSkirmishAILibrary(*(aiInfo->second));
343 }
344 }
345
346 return aiLib;
347 }
348
ReleaseSkirmishAILibrary(const SkirmishAIKey & skirmishAIKey)349 void CAILibraryManager::ReleaseSkirmishAILibrary(const SkirmishAIKey& skirmishAIKey) {
350
351 CAIInterfaceLibrary* interfaceLib = FetchInterface(skirmishAIKey.GetInterface());
352 if ((interfaceLib != NULL) && interfaceLib->IsInitialized()) {
353 interfaceLib->ReleaseSkirmishAILibrary(skirmishAIKey);
354 // only releases the library if its load count is 0
355 ReleaseInterface(skirmishAIKey.GetInterface());
356 } else {
357 // Not releasing, because the AI Interface is not initialized,
358 // and so neither was the AI.
359 }
360 }
361
362
ReleaseEverything()363 void CAILibraryManager::ReleaseEverything() {
364 T_loadedInterfaces::const_iterator lil;
365
366 for (lil = loadedAIInterfaceLibraries.begin(); lil != loadedAIInterfaceLibraries.end(); ++lil) {
367 CAIInterfaceLibrary* interfaceLib = FetchInterface(lil->first);
368 if ((interfaceLib != NULL) && interfaceLib->IsInitialized()) {
369 interfaceLib->ReleaseAllSkirmishAILibraries();
370 // only releases the library if its load count is 0
371 ReleaseInterface(lil->first);
372 }
373 }
374 }
375
376
377
FetchInterface(const AIInterfaceKey & interfaceKey)378 CAIInterfaceLibrary* CAILibraryManager::FetchInterface(const AIInterfaceKey& interfaceKey) {
379
380 CAIInterfaceLibrary* interfaceLib = NULL;
381
382 T_loadedInterfaces::const_iterator interfacePos = loadedAIInterfaceLibraries.find(interfaceKey);
383 if (interfacePos == loadedAIInterfaceLibraries.end()) { // interface not yet loaded
384 T_interfaceInfos::const_iterator interfaceInfo = interfaceInfos.find(interfaceKey);
385 if (interfaceInfo != interfaceInfos.end()) {
386 interfaceLib = new CAIInterfaceLibrary(*(interfaceInfo->second));
387 if (!interfaceLib->IsInitialized()) {
388 delete interfaceLib;
389 interfaceLib = NULL;
390 }
391 // storing this for later use, even if it is NULL (failed to init)
392 loadedAIInterfaceLibraries[interfaceKey] = interfaceLib;
393 } else {
394 // unavailable interface requested, returning NULL
395 }
396 } else {
397 interfaceLib = interfacePos->second;
398 }
399
400 return interfaceLib;
401 }
402
ReleaseInterface(const AIInterfaceKey & interfaceKey)403 void CAILibraryManager::ReleaseInterface(const AIInterfaceKey& interfaceKey) {
404
405 T_loadedInterfaces::iterator interfacePos = loadedAIInterfaceLibraries.find(interfaceKey);
406 if (interfacePos != loadedAIInterfaceLibraries.end()) {
407 CAIInterfaceLibrary* interfaceLib = interfacePos->second;
408 if (interfaceLib->GetLoadCount() == 0) {
409 loadedAIInterfaceLibraries.erase(interfacePos);
410 delete interfaceLib;
411 interfaceLib = NULL;
412 }
413 }
414 }
415
416
FindFittingInterfaceSpecifier(const std::string & shortName,const std::string & minVersion,const IAILibraryManager::T_interfaceSpecs & keys)417 AIInterfaceKey CAILibraryManager::FindFittingInterfaceSpecifier(
418 const std::string& shortName,
419 const std::string& minVersion,
420 const IAILibraryManager::T_interfaceSpecs& keys) {
421
422 std::set<AIInterfaceKey>::const_iterator key;
423 int minDiff = INT_MAX;
424 AIInterfaceKey fittingKey = AIInterfaceKey(); // unspecified key
425 for (key = keys.begin(); key != keys.end(); ++key) {
426 if (shortName == key->GetShortName()) {
427 int diff = IAILibraryManager::VersionCompare(key->GetVersion(), minVersion);
428 if (diff >= 0 && diff < minDiff) {
429 fittingKey = *key;
430 minDiff = diff;
431 }
432 }
433 }
434
435 return fittingKey;
436 }
437