|
1 /* $Id$ */ |
|
2 |
|
3 /** @file squirrel.cpp allows loading squirrel scripts to control an AI */ |
|
4 |
|
5 #include "../../stdafx.h" |
|
6 #include "../../debug.h" |
|
7 #include "../../openttd.h" |
|
8 #include "../../string.h" |
|
9 #include "../../fios.h" |
|
10 #include <sys/types.h> |
|
11 #include <sys/stat.h> |
|
12 |
|
13 #ifdef _UNICODE |
|
14 /* Disable unicode for squirrel to allow compilation with MINGW |
|
15 * and simplify coding for WIN32 (squirrel headers miss a lot of "string" functions) |
|
16 */ |
|
17 #undef _UNICODE |
|
18 #endif |
|
19 #include <squirrel.h> |
|
20 #include "../../squirrel.hpp" |
|
21 #include "../../squirrel_helper.hpp" |
|
22 #include "../../squirrel_class.hpp" |
|
23 #include "../core/ai_factory.hpp" |
|
24 #include "../core/ai_controller.hpp" |
|
25 #include "squirrel.hpp" |
|
26 |
|
27 /* Convert all AI related classes to Squirrel data */ |
|
28 #define DEFINE_SQUIRREL_CLASS |
|
29 #include "../core/ai_base.hpp" |
|
30 #include "../core/ai_cargo.hpp" |
|
31 #include "../core/ai_controller.hpp" |
|
32 #include "../core/ai_company.hpp" |
|
33 #include "../core/ai_industry.hpp" |
|
34 #include "../core/ai_map.hpp" |
|
35 #include "../core/ai_town.hpp" |
|
36 |
|
37 static FSquirrel iFSquirrel; ///< Tell the AI-core that we have an AI with which we like to play. |
|
38 |
|
39 /** |
|
40 * Our tiny wrapper between C++ and Squirrel. |
|
41 */ |
|
42 class AIFactorySquirrel: public AIFactoryBase { |
|
43 public: |
|
44 Squirrel *engine; |
|
45 HSQOBJECT SQ_instance; |
|
46 char *script_name; |
|
47 |
|
48 ~AIFactorySquirrel(); |
|
49 |
|
50 /* virtual */ const char *GetAuthor() { return this->engine->CallStringMethod (this->SQ_instance, "GetAuthor"); } |
|
51 /* virtual */ const char *GetName() { return this->engine->CallStringMethod (this->SQ_instance, "GetName"); } |
|
52 /* virtual */ const char *GetDescription() { return this->engine->CallStringMethod (this->SQ_instance, "GetDescription"); } |
|
53 /* virtual */ int GetVersion() { return this->engine->CallIntegerMethod(this->SQ_instance, "GetVersion"); } |
|
54 /* virtual */ const char *GetDate() { return this->engine->CallStringMethod (this->SQ_instance, "GetDate"); } |
|
55 /* virtual */ AIController *CreateInstance(); |
|
56 |
|
57 /** |
|
58 * Register Squirrel script to the Factory. |
|
59 */ |
|
60 void RegisterSquirrel() { this->RegisterFactory(this->GetName()); } |
|
61 |
|
62 /** |
|
63 * Check if a method exists in the script. |
|
64 */ |
|
65 void CheckMethods(AIFactorySquirrel *fbase, SQInteger *res, const char *name); |
|
66 }; |
|
67 |
|
68 AIFactorySquirrel::~AIFactorySquirrel() |
|
69 { |
|
70 free(this->script_name); |
|
71 } |
|
72 |
|
73 void AIFactorySquirrel::CheckMethods(AIFactorySquirrel *fbase, SQInteger *res, const char *name) |
|
74 { |
|
75 if (!fbase->engine->MethodExists(fbase->SQ_instance, name)) { |
|
76 char error[1024]; |
|
77 snprintf(error, sizeof(error), "Missing method '%s' for FactoryClass", name); |
|
78 fbase->engine->ThrowError(error); |
|
79 *res = SQ_ERROR; |
|
80 } |
|
81 } |
|
82 |
|
83 SQInteger FSquirrel::FactoryConstructor(HSQUIRRELVM vm) |
|
84 { |
|
85 AIFactorySquirrel *fbase = new AIFactorySquirrel(); |
|
86 SQInteger res = 0; |
|
87 |
|
88 Squirrel::GetInstance(vm, &fbase->SQ_instance); |
|
89 fbase->engine = ((FSquirrel *)Squirrel::GetGlobalPointer(vm))->engine; |
|
90 |
|
91 /* Check if all needed fields are there */ |
|
92 fbase->CheckMethods(fbase, &res, "GetAuthor"); |
|
93 fbase->CheckMethods(fbase, &res, "GetName"); |
|
94 fbase->CheckMethods(fbase, &res, "GetDescription"); |
|
95 fbase->CheckMethods(fbase, &res, "GetVersion"); |
|
96 fbase->CheckMethods(fbase, &res, "GetDate"); |
|
97 fbase->CheckMethods(fbase, &res, "CreateInstance"); |
|
98 |
|
99 /* Abort if one method was missing */ |
|
100 if (res != 0) return res; |
|
101 |
|
102 fbase->RegisterSquirrel(); |
|
103 fbase->script_name = strdup(((FSquirrel *)Squirrel::GetGlobalPointer(vm))->current_script); |
|
104 |
|
105 return 0; |
|
106 } |
|
107 |
|
108 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb); |
|
109 extern bool FiosIsHiddenFile(const struct dirent *ent); |
|
110 |
|
111 |
|
112 void FSquirrel::ScanDir(const char *dirname) |
|
113 { |
|
114 struct stat sb; |
|
115 struct dirent *dirent; |
|
116 DIR *dir; |
|
117 char d_name[256]; |
|
118 char script_name[256]; |
|
119 dir = ttd_opendir(dirname); |
|
120 /* Dir not found, so do nothing */ |
|
121 if (dir == NULL) return; |
|
122 |
|
123 /* Walk all dirs trying to find a dir in which 'main.nut' exists */ |
|
124 while ((dirent = readdir(dir)) != NULL) { |
|
125 ttd_strlcpy(d_name, FS2OTTD(dirent->d_name), sizeof(d_name)); |
|
126 |
|
127 /* Found file must be directory, but not '.' or '..' */ |
|
128 if (FiosIsValidFile("ai", dirent, &sb) && (sb.st_mode & S_IFDIR) && |
|
129 (!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) && |
|
130 strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) { |
|
131 /* Create the full-length script-name */ |
|
132 strcpy(script_name, dirname); |
|
133 strcat(script_name, PATHSEP); |
|
134 strcat(script_name, d_name); |
|
135 strcat(script_name, PATHSEP); |
|
136 strcat(script_name, "main.nut"); |
|
137 /* If it exists, load it up */ |
|
138 if (FileExists(script_name)) { |
|
139 DEBUG(ai, 6, "[squirrel] Loading script '%s' for AI handling", script_name); |
|
140 this->current_script = script_name; |
|
141 this->engine->LoadScript(this->current_script); |
|
142 } |
|
143 } |
|
144 } |
|
145 closedir(dir); |
|
146 } |
|
147 |
|
148 void FSquirrel::Initializer() |
|
149 { |
|
150 this->engine = new Squirrel(); |
|
151 |
|
152 /* Create the AIFactory class, and bind the constructor */ |
|
153 this->engine->AddClassBegin("AIFactory"); |
|
154 this->engine->AddMethod("constructor", &FSquirrel::FactoryConstructor, 1, "x"); |
|
155 this->engine->AddClassEnd(); |
|
156 |
|
157 /* Set a dummy AIController, so script can load */ |
|
158 this->engine->AddClassBegin("AIController"); |
|
159 this->engine->AddClassEnd(); |
|
160 /* Mark this class as global pointer */ |
|
161 this->engine->SetGlobalPointer(this); |
|
162 |
|
163 /* Scan the AI dir for scripts */ |
|
164 this->ScanDir("ai"); |
|
165 } |
|
166 |
|
167 FSquirrel::~FSquirrel() |
|
168 { |
|
169 delete this->engine; |
|
170 } |
|
171 |
|
172 AIController *AIFactorySquirrel::CreateInstance() |
|
173 { |
|
174 const char *class_name = this->engine->CallStringMethod(this->SQ_instance, "CreateInstance"); |
|
175 AIControllerSquirrel *controller = new AIControllerSquirrel(this->script_name, class_name); |
|
176 |
|
177 return controller; |
|
178 } |
|
179 |
|
180 void AIControllerSquirrel::RegisterClasses() |
|
181 { |
|
182 /* Ignore AIFactory if we are really starting an AI */ |
|
183 this->engine->AddClassBegin("AIFactory"); |
|
184 this->engine->AddClassEnd(); |
|
185 |
|
186 /* Register all classes */ |
|
187 SQAIBaseRegister(this->engine); |
|
188 SQAICargoRegister(this->engine); |
|
189 SQAICompanyRegister(this->engine); |
|
190 SQAIControllerRegister(this->engine); |
|
191 SQAIIndustryRegister(this->engine); |
|
192 SQAIMapRegister(this->engine); |
|
193 SQAITownRegister(this->engine); |
|
194 |
|
195 this->engine->SetGlobalPointer(this->engine); |
|
196 } |
|
197 |
|
198 AIControllerSquirrel::AIControllerSquirrel(const char *script, const char *class_name) |
|
199 { |
|
200 this->engine = new Squirrel(); |
|
201 this->RegisterClasses(); |
|
202 this->engine->LoadScript(script); |
|
203 /* Create the main-class */ |
|
204 this->engine->CreateClassInstance(class_name, this, &this->SQ_instance); |
|
205 this->engine->CallMethod(this->SQ_instance, "constructor"); |
|
206 } |
|
207 |
|
208 AIControllerSquirrel::~AIControllerSquirrel() |
|
209 { |
|
210 delete this->engine; |
|
211 } |
|
212 |
|
213 /* virtual */ void AIControllerSquirrel::GameLoop() |
|
214 { |
|
215 this->engine->CallMethod(this->SQ_instance, "GameLoop"); |
|
216 } |