(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. noai
authortruebrain
Tue, 20 May 2008 15:25:47 +0000
branchnoai
changeset 10650 30fc5395b1b8
parent 10649 9034b80fdbdb
child 10651 655c8b06f6d4
(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
bin/ai/regression/regression.nut
bin/ai/regression/regression_info.nut
bin/ai/regression/run.sh
bin/ai/wrightai/info.nut
bin/ai/wrightai/main.nut
src/ai/ai.cpp
src/ai/ai_gui.cpp
src/ai/ai_info.cpp
src/ai/ai_squirrel.cpp
src/ai/api/ai_controller.cpp
src/squirrel.cpp
src/squirrel.hpp
--- 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 */