1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3 #include "Interface.h"
4
5 #include "CUtils/Util.h"
6 #include "CUtils/SimpleLog.h"
7 #include "CUtils/SharedLibrary.h"
8
9 #include "ExternalAI/Interface/aidefines.h"
10 #include "ExternalAI/Interface/SAIInterfaceLibrary.h"
11 #include "ExternalAI/Interface/SSkirmishAILibrary.h"
12 #include "ExternalAI/Interface/SAIInterfaceCallback.h"
13 struct SSkirmishAICallback;
14
15 #include "System/Util.h"
16
CInterface(int interfaceId,const struct SAIInterfaceCallback * callback)17 CInterface::CInterface(int interfaceId,
18 const struct SAIInterfaceCallback* callback)
19 : interfaceId(interfaceId), callback(callback) {
20
21 char* logFileName = util_allocStrCatFSPath(2, "log", "interface-log.txt");
22 bool timeStamps = true;
23 #ifdef DEBUG
24 int logLevel = SIMPLELOG_LEVEL_FINE;
25 #else
26 int logLevel = SIMPLELOG_LEVEL_ERROR;
27 #endif
28 static const unsigned int logFilePath_sizeMax = 1024;
29 char logFilePath[logFilePath_sizeMax];
30 // eg: "~/.spring/AI/Interfaces/C/log/interface-log.txt"
31 bool ok = callback->DataDirs_locatePath(interfaceId,
32 logFilePath, logFilePath_sizeMax,
33 logFileName, true, true, false, false);
34 if (!ok) {
35 simpleLog_logL(SIMPLELOG_LEVEL_ERROR,
36 "Failed locating the log file %s.", logFileName);
37 }
38
39 simpleLog_init(logFilePath, timeStamps, logLevel, false);
40
41 const char* const myShortName = callback->AIInterface_Info_getValueByKey(interfaceId,
42 AI_INTERFACE_PROPERTY_SHORT_NAME);
43 const char* const myVersion = callback->AIInterface_Info_getValueByKey(interfaceId,
44 AI_INTERFACE_PROPERTY_VERSION);
45
46 simpleLog_log("This is the log-file of the %s v%s AI Interface",
47 myShortName, myVersion);
48 simpleLog_log("Using read/write data-directory: %s",
49 callback->DataDirs_getWriteableDir(interfaceId));
50 simpleLog_log("Using log file: %s", logFileName);
51
52 FREE(logFileName);
53 }
54
55 //LevelOfSupport CInterface::GetLevelOfSupportFor(
56 // const char* engineVersion, int engineAIInterfaceGeneratedVersion) {
57 // return LOS_Working;
58 //}
59
60 // SSkirmishAISpecifier CInterface::ExtractSpecifier(
61 // const std::map<std::string, std::string>& infoMap) {
62 //
63 // const char* const skirmDD =
64 // callback->SkirmishAIs_Info_getValueByKey(interfaceId,
65 // shortName, version,
66 // SKIRMISH_AI_PROPERTY_DATA_DIR);
67 // const char* sn = callback->AIInterface_Info_getValueByKey)(int interfaceId, const char* const key);
68 // .find(SKIRMISH_AI_PROPERTY_SHORT_NAME)->second.c_str();
69 // const char* v = infoMap.find(SKIRMISH_AI_PROPERTY_VERSION)->second.c_str();
70 //
71 // SSkirmishAISpecifier spec = {sn, v};
72 //
73 // return spec;
74 // }
75
LoadSkirmishAILibrary(const char * const shortName,const char * const version)76 const SSkirmishAILibrary* CInterface::LoadSkirmishAILibrary(
77 const char* const shortName,
78 const char* const version) {
79
80 SSkirmishAILibrary* ai = NULL;
81
82 SSkirmishAISpecifier spec;
83 spec.shortName = shortName;
84 spec.version = version;
85
86 mySkirmishAISpecifiers.insert(spec);
87
88 T_skirmishAIs::iterator skirmishAI;
89 skirmishAI = myLoadedSkirmishAIs.find(spec);
90 if (skirmishAI == myLoadedSkirmishAIs.end()) {
91 ai = new SSkirmishAILibrary;
92 sharedLib_t lib = Load(spec, ai);
93 if (sharedLib_isLoaded(lib)) {
94 // success
95 myLoadedSkirmishAIs[spec] = ai;
96 myLoadedSkirmishAILibs[spec] = lib;
97 } else {
98 // failure
99 delete ai;
100 ai = NULL;
101 }
102 } else {
103 ai = skirmishAI->second;
104 }
105
106 return ai;
107 }
UnloadSkirmishAILibrary(const char * const shortName,const char * const version)108 int CInterface::UnloadSkirmishAILibrary(
109 const char* const shortName,
110 const char* const version
111 ) {
112 SSkirmishAISpecifier spec;
113 spec.shortName = shortName;
114 spec.version = version;
115
116 T_skirmishAIs::iterator skirmishAI = myLoadedSkirmishAIs.find(spec);
117 T_skirmishAILibs::iterator skirmishAILib = myLoadedSkirmishAILibs.find(spec);
118
119 if (skirmishAI == myLoadedSkirmishAIs.end()) {
120 // to-unload-AI is not loaded -> no problem, do nothing
121 } else {
122 delete skirmishAI->second;
123 myLoadedSkirmishAIs.erase(skirmishAI);
124 sharedLib_unload(skirmishAILib->second);
125 myLoadedSkirmishAILibs.erase(skirmishAILib);
126 }
127
128 return 0;
129 }
UnloadAllSkirmishAILibraries()130 int CInterface::UnloadAllSkirmishAILibraries() {
131
132 while (!myLoadedSkirmishAIs.empty()) {
133 T_skirmishAISpecifiers::const_iterator ai =
134 mySkirmishAISpecifiers.begin();
135 UnloadSkirmishAILibrary((*ai).shortName, (*ai).version);
136 }
137
138 return 0; // signal: ok
139 }
140
141
142 // private functions following
143
Load(const SSkirmishAISpecifier & spec,SSkirmishAILibrary * skirmishAILibrary)144 sharedLib_t CInterface::Load(const SSkirmishAISpecifier& spec, SSkirmishAILibrary* skirmishAILibrary) {
145 return LoadSkirmishAILib(FindLibFile(spec), skirmishAILibrary);
146 }
LoadSkirmishAILib(const std::string & libFilePath,SSkirmishAILibrary * skirmishAILibrary)147 sharedLib_t CInterface::LoadSkirmishAILib(const std::string& libFilePath,
148 SSkirmishAILibrary* skirmishAILibrary) {
149
150 sharedLib_t sharedLib = sharedLib_load(libFilePath.c_str());
151
152 if (!sharedLib_isLoaded(sharedLib)) {
153 reportError(std::string("Failed loading shared library: ") + libFilePath);
154 return sharedLib;
155 }
156
157 // initialize the AI library
158 std::string funcName;
159
160 funcName = "getLevelOfSupportFor";
161 skirmishAILibrary->getLevelOfSupportFor
162 = (LevelOfSupport (CALLING_CONV_FUNC_POINTER *)(
163 const char* aiShortName, const char* aiVersion,
164 const char* engineVersionString, int engineVersionNumber,
165 const char* aiInterfaceShortName, const char* aiInterfaceVersion))
166 sharedLib_findAddress(sharedLib, funcName.c_str());
167 if (skirmishAILibrary->getLevelOfSupportFor == NULL) {
168 // do nothing: it is permitted that an AI does not export this function
169 //reportInterfaceFunctionError(libFilePath, funcName);
170 }
171
172 funcName = "init";
173 skirmishAILibrary->init
174 = (int (CALLING_CONV_FUNC_POINTER *)(int skirmishAIId,
175 const struct SSkirmishAICallback*))
176 sharedLib_findAddress(sharedLib, funcName.c_str());
177 if (skirmishAILibrary->init == NULL) {
178 // do nothing: it is permitted that an AI does not export this function,
179 // as it can still use EVENT_INIT instead
180 //reportInterfaceFunctionError(libFilePath, funcName);
181 }
182
183 funcName = "release";
184 skirmishAILibrary->release
185 = (int (CALLING_CONV_FUNC_POINTER *)(int skirmishAIId))
186 sharedLib_findAddress(sharedLib, funcName.c_str());
187 if (skirmishAILibrary->release == NULL) {
188 // do nothing: it is permitted that an AI does not export this function,
189 // as it can still use EVENT_RELEASE instead
190 //reportInterfaceFunctionError(libFilePath, funcName);
191 }
192
193 funcName = "handleEvent";
194 skirmishAILibrary->handleEvent
195 = (int (CALLING_CONV_FUNC_POINTER *)(int skirmishAIId,
196 int topicId, const void* data))
197 sharedLib_findAddress(sharedLib, funcName.c_str());
198 if (skirmishAILibrary->handleEvent == NULL) {
199 reportInterfaceFunctionError(libFilePath, funcName);
200 }
201
202 return sharedLib;
203 }
204
205
reportInterfaceFunctionError(const std::string & libFilePath,const std::string & functionName)206 void CInterface::reportInterfaceFunctionError(const std::string& libFilePath,
207 const std::string& functionName) {
208
209 std::string msg("Failed loading AI Library from file \"");
210 msg += libFilePath + "\": no \"" + functionName + "\" function exported";
211 reportError(msg);
212 }
213
reportError(const std::string & msg)214 void CInterface::reportError(const std::string& msg) {
215 simpleLog_logL(SIMPLELOG_LEVEL_ERROR, msg.c_str());
216 }
217
218
FindLibFile(const SSkirmishAISpecifier & spec)219 std::string CInterface::FindLibFile(const SSkirmishAISpecifier& spec) {
220
221 const char* const skirmDD =
222 callback->SkirmishAIs_Info_getValueByKey(interfaceId,
223 spec.shortName, spec.version,
224 SKIRMISH_AI_PROPERTY_DATA_DIR);
225 if (skirmDD == NULL) {
226 reportError(std::string("Missing Skirmish AI data-dir for ")
227 + spec.shortName + " " + spec.version);
228 }
229
230 static const size_t libFileName_sizeMax = 512;
231 // eg. "libSkirmishAI.so" or "SkirmishAI.dll"
232 char libFileName[libFileName_sizeMax];
233 sharedLib_createFullLibName("SkirmishAI", libFileName, libFileName_sizeMax);
234
235 return util_allocStrCatFSPath(2, skirmDD, libFileName);
236 }
237
FitsThisInterface(const std::string & requestedShortName,const std::string & requestedVersion)238 bool CInterface::FitsThisInterface(const std::string& requestedShortName,
239 const std::string& requestedVersion) {
240
241 const char* const myShortName = callback->AIInterface_Info_getValueByKey(interfaceId,
242 AI_INTERFACE_PROPERTY_SHORT_NAME);
243 const char* const myVersion = callback->AIInterface_Info_getValueByKey(interfaceId,
244 AI_INTERFACE_PROPERTY_VERSION);
245
246 bool shortNameFits = (requestedShortName == myShortName);
247 bool versionFits = (requestedVersion == myVersion);
248
249 return shortNameFits && versionFits;
250 }
251