(svn r13194) [NoAI] -Change [API CHANGE]: split 'main.nut' in 'info.nut' and 'main.nut'. The first contains the information about the AI, the second the AI. This avoid several problems we had. It also speeds up OpenTTD start-up.
See: http://wiki.openttd.org/index.php/AI:AIInfo for more info.
[NoAI] -Fix: several bug-fixes and memory-leak-fixes
[NoAI] -Fix: now compile-errors are send to the AIDebug window
--- a/bin/ai/regression/regression.nut Tue May 20 13:09:34 2008 +0000
+++ b/bin/ai/regression/regression.nut Tue May 20 15:25:47 2008 +0000
@@ -1279,18 +1279,3 @@
this.stop = true;
}
-class FRegression extends AIFactory {
- function GetAuthor() { return "OpenTTD Dev Team"; }
- function GetName() { return "Regression"; }
- function GetDescription() { return "This runs regression-tests on all commands. On the same map the result should always be the same."; }
- function GetVersion() { return 1; }
- function GetDate() { return "2007-03-18"; }
- function CreateInstance() { return "Regression"; }
-}
-
-/* Only enable this if you want to run the regression test.
- * The best way to do it is to disable all other AIs, so you are sure thisone
- * kicks in, and use a pre-made savegame of which you already know the result.
- * You can compare the output from this AI with the pre-made one, and it should
- * match. If not, something went wrong. */
-iFRegression <-FRegression();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/ai/regression/regression_info.nut Tue May 20 15:25:47 2008 +0000
@@ -0,0 +1,11 @@
+class Regression extends AIInfo {
+ function GetAuthor() { return "OpenTTD Dev Team"; }
+ function GetName() { return "Regression"; }
+ function GetDescription() { return "This runs regression-tests on all commands. On the same map the result should always be the same."; }
+ function GetVersion() { return 1; }
+ function GetDate() { return "2007-03-18"; }
+ function CreateInstance() { return "Regression"; }
+}
+
+RegisterAI(Regression());
+
--- a/bin/ai/regression/run.sh Tue May 20 13:09:34 2008 +0000
+++ b/bin/ai/regression/run.sh Tue May 20 15:25:47 2008 +0000
@@ -6,6 +6,7 @@
fi
cp ai/regression/regression.nut ai/regression/main.nut
+cp ai/regression/regression_info.nut ai/regression/info.nut
params=""
gdb=""
@@ -33,7 +34,7 @@
echo "Regression test done"
fi
-rm -f ai/regression/main.nut
+rm -f ai/regression/main.nut ai/regression/info.nut
if [ "$1" != "-k" ]; then
rm -f tmp.regression
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/ai/wrightai/info.nut Tue May 20 15:25:47 2008 +0000
@@ -0,0 +1,10 @@
+class WrightAI extends AIInfo {
+ function GetAuthor() { return "OpenTTD Dev Team"; }
+ function GetName() { return "WrightAI"; }
+ function GetDescription() { return "A simple AI that tries to beat you with only aircrafts"; }
+ function GetVersion() { return 1.1; }
+ function GetDate() { return "2008-02-24"; }
+ function CreateInstance() { return "WrightAI"; }
+}
+
+RegisterAI(WrightAI());
--- a/bin/ai/wrightai/main.nut Tue May 20 13:09:34 2008 +0000
+++ b/bin/ai/wrightai/main.nut Tue May 20 15:25:47 2008 +0000
@@ -377,14 +377,3 @@
{
}
-class FWrightAI extends AIFactory {
- function GetAuthor() { return "OpenTTD Dev Team"; }
- function GetName() { return "WrightAI"; }
- function GetDescription() { return "A simple AI that tries to beat you with only aircrafts"; }
- function GetVersion() { return 1.1; }
- function GetDate() { return "2008-02-24"; }
- function CreateInstance() { return "WrightAI"; }
-}
-
-/* Tell the core we are an AI */
-iFWrightAI <- FWrightAI();
--- a/src/ai/ai.cpp Tue May 20 13:09:34 2008 +0000
+++ b/src/ai/ai.cpp Tue May 20 15:25:47 2008 +0000
@@ -107,10 +107,10 @@
return;
}
+ _current_player = player;
_ai_info[player] = info;
+ AIObject::ResetInternalPlayerData();
_ai_player[player] = info->CreateInstance();
- _current_player = player;
- AIObject::ResetInternalPlayerData();
AI_StartPlayer(player, _ai_player[player]);
}
--- a/src/ai/ai_gui.cpp Tue May 20 13:09:34 2008 +0000
+++ b/src/ai/ai_gui.cpp Tue May 20 15:25:47 2008 +0000
@@ -150,7 +150,7 @@
for (int i = 0; i < log->count; i++) {
uint pos = (log->count + log->pos - i) % log->count;
- if (log->type[pos] == 0) break;
+ if (log->lines[pos] == NULL) break;
int y = 12 * i;
if (y >= this->widget[AID_WIDGET_LOG_PANEL].bottom - this->widget[AID_WIDGET_LOG_PANEL].top - 12) break;
--- a/src/ai/ai_info.cpp Tue May 20 13:09:34 2008 +0000
+++ b/src/ai/ai_info.cpp Tue May 20 15:25:47 2008 +0000
@@ -16,7 +16,7 @@
AIInfo::~AIInfo()
{
- this->base->UnregisterAI(this);
+ this->engine->ReleaseObject(this->SQ_instance);
free(this->script_name);
free(this->dir_name);
free(this->SQ_instance);
@@ -72,7 +72,7 @@
{
if (!this->engine->MethodExists(*this->SQ_instance, name)) {
char error[1024];
- snprintf(error, sizeof(error), "Missing method '%s' for FactoryClass", name);
+ snprintf(error, sizeof(error), "your AIInfo doesn't have the method '%s'", name);
this->engine->ThrowError(error);
*res = SQ_ERROR;
}
@@ -86,7 +86,9 @@
/* Set some basic info from the parent */
info->SQ_instance = MallocT<SQObject>(sizeof(SQObject));
- Squirrel::GetInstance(vm, info->SQ_instance);
+ Squirrel::GetInstance(vm, info->SQ_instance, 2);
+ /* Make sure the instance stays alive over time */
+ sq_addref(vm, info->SQ_instance);
info->base = ((AISquirrel *)Squirrel::GetGlobalPointer(vm));
info->engine = info->base->GetEngine();
@@ -101,9 +103,10 @@
/* Abort if one method was missing */
if (res != 0) return res;
- /* Register the AI to the base system */
info->script_name = strdup(info->base->GetCurrentScript());
info->dir_name = strdup(info->base->GetCurrentDirName());
+
+ /* Register the AI to the base system */
info->base->RegisterAI(info);
return 0;
--- a/src/ai/ai_squirrel.cpp Tue May 20 13:09:34 2008 +0000
+++ b/src/ai/ai_squirrel.cpp Tue May 20 15:25:47 2008 +0000
@@ -64,13 +64,28 @@
*ext = '\0';
}
- /* We look for the file 'main.nut' inside the AI dir.. if it doesn't exists, it isn't an AI */
- ttd_strlcat(script_name, "main.nut", sizeof(script_name));
+ /* We look for the file 'info.nut' inside the AI dir.. if it doesn't exists, it isn't an AI */
+ ttd_strlcat(script_name, "info.nut", sizeof(script_name));
if (FioCheckFileExists(script_name, AI_DIR)) {
- DEBUG(ai, 6, "[squirrel] Loading script '%s' for AI handling", script_name);
+ char load_script[MAX_PATH];
+ snprintf(load_script, sizeof(load_script), "%s", script_name);
+
+ /* Remove the 'info.nut' part and replace it with 'main.nut' */
+ script_name[strlen(script_name) - 8] = '\0';
+ ttd_strlcat(script_name, "main.nut", sizeof(script_name));
+
+ DEBUG(ai, 6, "[squirrel] Loading script '%s' for AI handling", load_script);
this->current_script = script_name;
this->current_dir = d_name;
- this->engine->LoadScript(this->current_script);
+ this->engine->LoadScript(load_script);
+ } else {
+ /* Check if this person does have main.nut, but not yet info.nut, and assume it is an old AI
+ * Notice that the 'info' -> 'main' change is already done above. */
+ if (FioCheckFileExists(script_name, AI_DIR)) {
+ DEBUG(ai, 0, "You are using a deprecated way of registering your AI.");
+ DEBUG(ai, 0, "Please read http://wiki.openttd.org/index.php/AI:AIInfo for more info");
+ DEBUG(ai, 0, "Your AI will _not_ be loaded.");
+ }
}
}
closedir(dir);
@@ -80,14 +95,10 @@
{
this->engine = new Squirrel();
- /* Create the AIFactory class, and bind the constructor */
- this->engine->AddClassBegin("AIFactory");
- this->engine->AddMethod("constructor", &AIInfo::Constructor, 1, "x");
+ /* Create the AIInfo class, and add the RegisterAI function */
+ this->engine->AddClassBegin("AIInfo");
this->engine->AddClassEnd();
-
- /* Set a dummy AIController, so script can load */
- this->engine->AddClassBegin("AIController");
- this->engine->AddClassEnd();
+ this->engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
/* Mark this class as global pointer */
this->engine->SetGlobalPointer(this);
@@ -104,6 +115,12 @@
AISquirrel::~AISquirrel()
{
+ AIInfoList::iterator it = this->info_list.begin();
+ for (; it != this->info_list.end(); it++) {
+ AIInfo *i = (*it).second;
+ delete i;
+ }
+
delete this->engine;
}
@@ -111,16 +128,17 @@
{
const char *ai_name = info->GetDirName();
- /* Check if we register twice; than the latest always wins */
+ /* Check if we register twice; than the first always wins */
if (this->info_list.find(ai_name) != this->info_list.end()) {
/* In case they are not the same dir, give a warning */
if (strcasecmp(info->GetScriptName(), this->info_list[ai_name]->GetScriptName()) != 0) {
DEBUG(ai, 0, "Registering two AIs with the same name");
DEBUG(ai, 0, " 1: %s", this->info_list[ai_name]->GetScriptName());
DEBUG(ai, 0, " 2: %s", info->GetScriptName());
- DEBUG(ai, 0, "The latter is taking precedence");
+ DEBUG(ai, 0, "The first is taking precedence");
}
- this->info_list[ai_name] = info;
+ /* Delete the new AIInfo, as we will be using the old one */
+ delete info;
return;
}
this->info_list[ai_name] = info;
--- a/src/ai/api/ai_controller.cpp Tue May 20 13:09:34 2008 +0000
+++ b/src/ai/api/ai_controller.cpp Tue May 20 15:25:47 2008 +0000
@@ -71,10 +71,6 @@
void AIController::RegisterClasses()
{
- /* Ignore AIFactory if we are really starting an AI */
- this->engine->AddClassBegin("AIFactory");
- this->engine->AddClassEnd();
-
/* Register all classes */
squirrel_register_std(this->engine);
SQAIAbstractList_Register(this->engine);
@@ -151,6 +147,7 @@
AIController::AIController(const char *script, const char *class_name)
{
this->tick = 0;
+ this->SQ_instance = NULL;
/* Create the Squirrel Engine and assign all required classes and settings */
this->engine = new Squirrel();
@@ -163,7 +160,11 @@
}
/* Create the main-class */
this->SQ_instance = MallocT<SQObject>(sizeof(SQObject));
- this->engine->CreateClassInstance(class_name, this, this->SQ_instance);
+ if (!this->engine->CreateClassInstance(class_name, this, this->SQ_instance)) {
+ delete this->engine;
+ this->engine = NULL;
+ return;
+ }
if (this->engine->MethodExists(*this->SQ_instance, "constructor"))
this->engine->CallMethod(*this->SQ_instance, "constructor");
}
@@ -180,7 +181,7 @@
AIController::~AIController()
{
- delete this->engine;
+ if (this->engine != NULL) delete this->engine;
free(this->SQ_instance);
}
--- a/src/squirrel.cpp Tue May 20 13:09:34 2008 +0000
+++ b/src/squirrel.cpp Tue May 20 15:25:47 2008 +0000
@@ -13,15 +13,21 @@
void Squirrel::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column)
{
- char *src = strdup(FS2OTTD(source));
- char *dsc = strdup(FS2OTTD(desc));
+ SQChar buf[1024];
+
#ifdef _SQ64
- DEBUG(misc, 0, "Error %s:%ld/%ld: %s", src, line, column, dsc);
+ scsnprintf(buf, lengthof(buf), _SC("Error %s:%ld/%ld: %s"), source, line, column, desc);
#else
- DEBUG(misc, 0, "Error %s:%d/%d: %s", src, line, column, dsc);
+ scsnprintf(buf, lengthof(buf), _SC("Error %s:%d/%d: %s"), source, line, column, desc);
#endif
- free(src);
- free(dsc);
+
+ /* Check if we have a custom print function */
+ SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func;
+ if (func == NULL) {
+ scfprintf(stderr, _SC("%s"), buf);
+ } else {
+ (*func)(true, buf);
+ }
}
void Squirrel::ErrorPrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
--- a/src/squirrel.hpp Tue May 20 13:09:34 2008 +0000
+++ b/src/squirrel.hpp Tue May 20 15:25:47 2008 +0000
@@ -126,7 +126,7 @@
* @note This will only work just after a function-call from within Squirrel
* to your C++ function.
*/
- static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr) { sq_getclass(vm, 1); sq_getstackobj(vm, 1, ptr); sq_pop(vm, 1); return true; }
+ static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos = 1) { sq_getclass(vm, pos); sq_getstackobj(vm, pos, ptr); sq_pop(vm, 1); return true; }
/**
* Convert a Squirrel-object to a string.
@@ -158,6 +158,11 @@
* Throw a Squirrel error that will be nicely displayed to the user.
*/
void ThrowError(const char *error) { sq_throwerror(this->vm, OTTD2FS(error)); }
+
+ /**
+ * Release a SQ object.
+ */
+ void ReleaseObject(HSQOBJECT *ptr) { sq_release(this->vm, ptr); }
};
#endif /* SQUIRREL_HPP */