(svn r13440) [NoAI] -Add: introducing ai/library, a method to load libraries into your AI. noai
authortruebrain
Tue, 10 Jun 2008 14:11:35 +0000
branchnoai
changeset 10889 ecb77cfc4a10
parent 10888 8a74bb2691b0
child 10890 1d8f2baff829
(svn r13440) [NoAI] -Add: introducing ai/library, a method to load libraries into your AI.
-Note: to use a library, use in your AI: import("categorie.name", "LocalName", version_number_of_library)
-Example: import("sets.priority_queue", "PQ", 1); local pq = PQ(2); pq.insert(2, 1);
[NoAI] -Add [Library]: sets.priority_queue (based on work by Morloth)
bin/ai/library/sets/priority_queue/library.nut
bin/ai/library/sets/priority_queue/main.nut
bin/ai/regression/regression.nut
bin/ai/regression/regression.txt
src/ai/ai.cpp
src/ai/ai.h
src/ai/ai_info.cpp
src/ai/ai_info.hpp
src/ai/ai_squirrel.cpp
src/ai/ai_squirrel.hpp
src/ai/api/ai_controller.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/ai/library/sets/priority_queue/library.nut	Tue Jun 10 14:11:35 2008 +0000
@@ -0,0 +1,12 @@
+/* $Id$ */
+
+class PriorityQueue extends AILibrary {
+	function GetAuthor()      { return "OpenTTD NoAI Developers Team"; }
+	function GetName()        { return "Priority Queue"; }
+	function GetDescription() { return "An implementation of a Priority Queue"; }
+	function GetVersion()     { return 1; }
+	function GetDate()        { return "2008-06-10"; }
+	function CreateInstance() { return "PriorityQueue"; }
+}
+
+RegisterLibrary(PriorityQueue());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/ai/library/sets/priority_queue/main.nut	Tue Jun 10 14:11:35 2008 +0000
@@ -0,0 +1,113 @@
+/* $Id$ */
+
+/**
+ * Priority Queue
+ *  Peek and Remove always return the current lowest value in the list.
+ *  Sort is done on insertion. When initial size runs out, array is
+ *  resized to be twice as big.
+ */
+class PriorityQueue {
+	_queue = null;
+	_count = 0;
+	_size = 0;
+
+	constructor(size)
+	{
+		this._count = 0;
+		this._queue = array(size * 2);
+		this._size = size * 2;
+	}
+
+	/**
+	 * Insert a new entry in the list.
+	 * @param item The item to add to the list.
+	 * @param priority The priority this item has.
+	 */
+	function Insert(item, priority);
+
+	/**
+	 * Pop the first entry of the list.
+	 *  This is always the item with the lowest priority.
+	 * @return The item of the entry with the lowest priority.
+	 */
+	function Pop();
+
+	/**
+	 * Peek the first entry of the list.
+	 *  This is always the item with the lowest priority.
+	 * @return The item of the entry with the lowest priority.
+	 */
+	function Peek();
+
+	/**
+	 * Get the amount of current items in the list.
+	 * @return The amount of items currently in the list.
+	 */
+	function Count();
+}
+
+function PriorityQueue::_checkAndExpand()
+{
+	if (this._count != this._size) return;
+
+	/* Create a new array twice the size */
+	this._size = this._size * 2;
+	local tmpArray = array(this._size);
+
+	/* Copy all elements */
+	for (local i = 0; i < this._count; i++) {
+		tmpArray[i] = this._queue[i];
+	}
+
+	this._queue = tmpArray;
+}
+
+function PriorityQueue::Insert(item, priority)
+{
+	this._checkAndExpand();
+
+	/* Find a place to insert the data */
+	if (this._count == 0) {
+		this._queue[this._count] = priority;
+		this._queue[this._count + 1] = item;
+		this._count += 2;
+	} else {
+		local i;
+		for (i = this._count - 2; i >= 0; i -= 2) {
+			if (priority > this._queue[i]) {
+				/* All items bigger move one place to the right */
+				this._queue[i + 2] = this._queue[i];
+				this._queue[i + 3] = this._queue[i + 1];
+			} else if (item == this._queue[i + 1]) {
+				/* Same item, ignore insertion */
+				return false;
+			} else {
+				/* Found place to insert at */
+				break;
+			}
+		}
+		this._queue[i + 2] = priority
+		this._queue[i + 3] = item;
+		this._count += 2;
+	}
+
+	return true;
+}
+
+function PriorityQueue::Pop()
+{
+	if (this._count == 0) return null;
+	this._count -= 2;
+	return this._queue[this._count + 1];
+}
+
+function PriorityQueue::Peek()
+{
+	if (this._count == 0) return null;
+	return this._queue[this._count - 1];
+}
+
+function PriorityQueue::Count()
+{
+	return this._count / 2;
+}
--- a/bin/ai/regression/regression.nut	Tue Jun 10 14:08:39 2008 +0000
+++ b/bin/ai/regression/regression.nut	Tue Jun 10 14:11:35 2008 +0000
@@ -1,3 +1,5 @@
+import("sets.priority_queue", "PQ", 1);
+
 class Regression extends AIController {
 	function Start();
 }
@@ -23,6 +25,26 @@
 	print("--Std--");
 	print(" abs(-21): " + abs(-21));
 	print(" abs( 21): " + abs(21));
+
+	print("");
+	print("--PriorityQueue--");
+	local pq = PQ(2);
+	pq.Insert(1, 20);
+	pq.Insert(2, 40);
+	pq.Insert(3, 10);
+	pq.Insert(4, 15);
+	pq.Insert(5, 60);
+	pq.Insert(6, 5);
+	print("  Count(): " + pq.Count());
+	print("  Peek():  " + pq.Peek());
+	print("  Pop():   " + pq.Pop());
+	print("  Pop():   " + pq.Pop())
+	print("  Pop():   " + pq.Pop())
+	print("  Pop():   " + pq.Pop())
+	print("  Pop():   " + pq.Pop())
+	print("  Pop():   " + pq.Pop())
+	print("  Peek():  " + pq.Peek());
+	print("  Pop():   " + pq.Pop());
 }
 
 function Regression::Base()
--- a/bin/ai/regression/regression.txt	Tue Jun 10 14:08:39 2008 +0000
+++ b/bin/ai/regression/regression.txt	Tue Jun 10 14:11:35 2008 +0000
@@ -11,6 +11,18 @@
  abs(-21): 21
  abs( 21): 21
 
+--PriorityQueue--
+  Count(): 6
+  Peek():  6
+  Pop():   6
+  Pop():   3
+  Pop():   4
+  Pop():   1
+  Pop():   2
+  Pop():   5
+  Peek():  (null : 0x00000000)
+  Pop():   (null : 0x00000000)
+
 --AIBase--
   Rand():       753450495
   Rand():       202826571
--- a/src/ai/ai.cpp	Tue Jun 10 14:08:39 2008 +0000
+++ b/src/ai/ai.cpp	Tue Jun 10 14:11:35 2008 +0000
@@ -216,6 +216,11 @@
 	return true;
 }
 
+bool AI_ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm)
+{
+	return _ai_squirrel->ImportLibrary(library, class_name, version, vm);
+}
+
 #else /* NO_THREADS */
 /* Stub functions for when we do not have threads. */
 void AI_StartNewAI(PlayerID player) { DEBUG(ai, 0, "Threading is disabled and therefor the AI too."); }
@@ -228,5 +233,6 @@
 void AI_ForceAI(const char *forced_ai) {}
 char *AI_GetConsoleList(char *p, const char *last) { return p; }
 AIInfo *AI_GetPlayerInfo(PlayerID player) { return NULL; }
+bool AI_LoadLibrary(const char *library, HSQUIRRELVM vm) { return false; }
 void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2) {}
 #endif /* NO_THREADS */
--- a/src/ai/ai.h	Tue Jun 10 14:08:39 2008 +0000
+++ b/src/ai/ai.h	Tue Jun 10 14:11:35 2008 +0000
@@ -17,6 +17,7 @@
 void AI_ForceAI(const char *forced_ai);
 void AI_Event(PlayerID player, AIEvent *event);
 char *AI_GetConsoleList(char *p, const char *last);
+bool AI_ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm);
 
 void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2);
 
--- a/src/ai/ai_info.cpp	Tue Jun 10 14:08:39 2008 +0000
+++ b/src/ai/ai_info.cpp	Tue Jun 10 14:11:35 2008 +0000
@@ -1,6 +1,6 @@
 /* $Id$ */
 
-/** @file ai_info.cpp Implementation of AIInfo */
+/** @file ai_info.cpp Implementation of AIFileInfo */
 
 #include "../stdafx.h"
 #include "../core/alloc_func.hpp"
@@ -10,88 +10,92 @@
 #include "../squirrel_helper.hpp"
 #include "../squirrel_class.hpp"
 #include "../squirrel_std.hpp"
+#include "ai.h"
 #include "ai_info.hpp"
 #include "ai_squirrel.hpp"
 #include "api/ai_controller.hpp"
 
-AIInfo::~AIInfo()
+AIFileInfo::~AIFileInfo()
 {
 	this->engine->ReleaseObject(this->SQ_instance);
 	free((void *)this->author);
 	free((void *)this->name);
 	free((void *)this->description);
 	free((void *)this->date);
+	free((void *)this->instance_name);
 	free(this->script_name);
 	free(this->dir_name);
 	free(this->SQ_instance);
 }
 
-const char *AIInfo::GetAuthor()
+const char *AIFileInfo::GetAuthor()
 {
 	if (this->author == NULL) this->author = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetAuthor");
 	return this->author;
 }
 
-const char *AIInfo::GetName()
+const char *AIFileInfo::GetName()
 {
 	if (this->name == NULL) this->name = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetName");
 	return this->name;
 }
 
-const char *AIInfo::GetDescription()
+const char *AIFileInfo::GetDescription()
 {
 	if (this->description == NULL) this->description = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetDescription");
 	return this->description;
 }
 
-int AIInfo::GetVersion()
+int AIFileInfo::GetVersion()
 {
 	return this->engine->CallIntegerMethod(*this->SQ_instance, "GetVersion");
 }
 
-const char *AIInfo::GetDate()
+const char *AIFileInfo::GetDate()
 {
 	if (this->date == NULL) this->date = this->engine->CallStringMethodStrdup(*this->SQ_instance, "GetDate");
 	return this->date;
 }
 
-bool AIInfo::AllowStartup()
+const char *AIFileInfo::GetInstanceName()
+{
+	if (this->instance_name == NULL) this->instance_name = this->engine->CallStringMethodStrdup(*this->SQ_instance, "CreateInstance");
+	return this->instance_name;
+}
+
+bool AIFileInfo::AllowStartup()
 {
 	return true;
 }
 
-AIController *AIInfo::CreateInstance()
+AIController *AIFileInfo::CreateInstance()
 {
-	const char *class_name = this->engine->CallStringMethodStrdup(*this->SQ_instance, "CreateInstance");
-	AIController *ai_controller = new AIController(this->script_name, class_name);
-	free((void *)class_name);
+	AIController *ai_controller = new AIController(this->script_name, this->GetInstanceName());
 	return ai_controller;
 }
 
-const char *AIInfo::GetDirName()
+const char *AIFileInfo::GetDirName()
 {
 	return this->dir_name;
 }
 
-const char *AIInfo::GetScriptName()
+const char *AIFileInfo::GetScriptName()
 {
 	return this->script_name;
 }
 
-void AIInfo::CheckMethods(SQInteger *res, const char *name)
+void AIFileInfo::CheckMethods(SQInteger *res, const char *name)
 {
 	if (!this->engine->MethodExists(*this->SQ_instance, name)) {
 		char error[1024];
-		snprintf(error, sizeof(error), "your AIInfo doesn't have the method '%s'", name);
+		snprintf(error, sizeof(error), "your AIFileInfo doesn't have the method '%s'", name);
 		this->engine->ThrowError(error);
 		*res = SQ_ERROR;
 	}
 }
 
-/* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm)
+/* static */ SQInteger AIFileInfo::Constructor(HSQUIRRELVM vm, AIFileInfo *info)
 {
-	/* Create a new AIInfo */
-	AIInfo *info = new AIInfo();
 	SQInteger res = 0;
 
 	/* Set some basic info from the parent */
@@ -116,8 +120,44 @@
 	info->script_name = strdup(info->base->GetCurrentScript());
 	info->dir_name = strdup(info->base->GetCurrentDirName());
 
+	return 0;
+}
+
+/* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm)
+{
+	/* Create a new AIFileInfo */
+	AIInfo *info = new AIInfo();
+
+	SQInteger res = AIFileInfo::Constructor(vm, info);
+	if (res != 0) return res;
+
 	/* Register the AI to the base system */
 	info->base->RegisterAI(info);
 
 	return 0;
 }
+
+/* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
+{
+	/* Create a new AIFileInfo */
+	AILibrary *library = new AILibrary();
+
+	SQInteger res = AIFileInfo::Constructor(vm, library);
+	if (res != 0) return res;
+
+	/* Register the Library to the base system */
+	library->base->RegisterLibrary(library);
+
+	return 0;
+}
+
+/* static */ SQInteger AILibrary::Import(HSQUIRRELVM vm)
+{
+	SQConvert::SQAutoFreePointers ptr;
+	const char *library = GetParam(SQConvert::ForceType<const char *>(), vm, 2, &ptr);
+	const char *class_name = GetParam(SQConvert::ForceType<const char *>(), vm, 3, &ptr);
+	int version = GetParam(SQConvert::ForceType<int>(), vm, 4, &ptr);
+
+	if (!AI_ImportLibrary(library, class_name, version, vm)) return -1;
+	return 0;
+}
--- a/src/ai/ai_info.hpp	Tue Jun 10 14:08:39 2008 +0000
+++ b/src/ai/ai_info.hpp	Tue Jun 10 14:11:35 2008 +0000
@@ -5,10 +5,13 @@
 #ifndef AI_INFO
 #define AI_INFO
 
-class AIInfo {
+class AIFileInfo {
 public:
-	AIInfo() : author(NULL), name(NULL), description(NULL), date(NULL) {};
-	~AIInfo();
+	friend class AIInfo;
+	friend class AILibrary;
+
+	AIFileInfo() : author(NULL), name(NULL), description(NULL), date(NULL), instance_name(NULL) {};
+	~AIFileInfo();
 
 	/**
 	 * Get the Author of the AI.
@@ -36,6 +39,11 @@
 	const char *GetDate();
 
 	/**
+	 * Get the name of the instance of the AI to create.
+	 */
+	const char *GetInstanceName();
+
+	/**
 	 * Check if we can start this AI.
 	 */
 	bool AllowStartup();
@@ -61,9 +69,9 @@
 	void CheckMethods(SQInteger *res, const char *name);
 
 	/**
-	 * Create an AI, using this AIInfo as start-template.
+	 * Process the creation of a FileInfo object.
 	 */
-	static SQInteger Constructor(HSQUIRRELVM vm);
+	static SQInteger Constructor(HSQUIRRELVM vm, AIFileInfo *info);
 
 private:
 	Squirrel *engine;
@@ -75,6 +83,25 @@
 	const char *name;
 	const char *description;
 	const char *date;
+	const char *instance_name;
+};
+
+class AIInfo : public AIFileInfo {
+public:
+	/**
+	 * Create an AI, using this AIInfo as start-template.
+	 */
+	static SQInteger Constructor(HSQUIRRELVM vm);
+};
+
+class AILibrary : public AIFileInfo {
+public:
+	/**
+	 * Create an AI, using this AIInfo as start-template.
+	 */
+	static SQInteger Constructor(HSQUIRRELVM vm);
+
+	static SQInteger Import(HSQUIRRELVM vm);
 };
 
 #endif /* AI_INFO */
--- a/src/ai/ai_squirrel.cpp	Tue Jun 10 14:08:39 2008 +0000
+++ b/src/ai/ai_squirrel.cpp	Tue Jun 10 14:11:35 2008 +0000
@@ -18,7 +18,7 @@
 #include "ai_info.hpp"
 #include "ai_squirrel.hpp"
 
-void AISquirrel::ScanDir(const char *dirname)
+void AISquirrel::ScanDir(const char *dirname, bool library_dir, char *library_depth)
 {
 	extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
 	extern bool FiosIsHiddenFile(const struct dirent *ent);
@@ -46,6 +46,11 @@
 			ttd_strlcpy(script_name, dirname, sizeof(script_name));
 			ttd_strlcat(script_name, d_name, sizeof(script_name));
 			ttd_strlcat(script_name, PATHSEP, sizeof(script_name));
+
+			if (library_dir && library_depth == NULL) {
+				ScanDir(script_name, library_dir, d_name);
+				continue;
+			}
 		}
 		if (sb.st_mode & S_IFREG) {
 			char *ext = strrchr(d_name, '.');
@@ -64,20 +69,42 @@
 			*ext = '\0';
 		}
 
-		/* 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)) {
-			char load_script[MAX_PATH];
-			snprintf(load_script, sizeof(load_script), "%s", script_name);
+		if (!library_dir) {
+			/* 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)) {
+				char load_script[MAX_PATH];
+				ttd_strlcpy(load_script, script_name, sizeof(load_script));
 
-			/* 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));
+				/* 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(load_script);
+				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(load_script);
+			}
+		} else {
+			/* We look for the file 'library.nut' inside the library dir.. */
+			ttd_strlcat(script_name, "library.nut", sizeof(script_name));
+			if (FioCheckFileExists(script_name, AI_DIR)) {
+				char load_script[MAX_PATH];
+				char dir_name[MAX_PATH];
+				ttd_strlcpy(load_script, script_name, sizeof(load_script));
+				ttd_strlcpy(dir_name, library_depth, sizeof(dir_name));
+				ttd_strlcat(dir_name, ".", sizeof(dir_name));
+				ttd_strlcat(dir_name, d_name, sizeof(dir_name));
+
+				/* Remove the 'library.nut' part and replace it with 'main.nut' */
+				script_name[strlen(script_name) - 11] = '\0';
+				ttd_strlcat(script_name, "main.nut", sizeof(script_name));
+
+				DEBUG(ai, 6, "[squirrel] Loading script '%s' for Squirrel library", load_script);
+				this->current_script = script_name;
+				this->current_dir = dir_name;
+				this->engine->LoadScript(load_script);
+			}
 		}
 	}
 	closedir(dir);
@@ -85,12 +112,17 @@
 
 AISquirrel::AISquirrel()
 {
+	this->library_instance_count = 0;
 	this->engine = new Squirrel();
 
 	/* Create the AIInfo class, and add the RegisterAI function */
 	this->engine->AddClassBegin("AIInfo");
 	this->engine->AddClassEnd();
 	this->engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
+	/* Create the AILibrary class, and add the RegisterLibrary function */
+	this->engine->AddClassBegin("AILibrary");
+	this->engine->AddClassEnd();
+	this->engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx");
 
 	/* Mark this class as global pointer */
 	this->engine->SetGlobalPointer(this);
@@ -101,7 +133,9 @@
 
 	FOR_ALL_SEARCHPATHS(sp) {
 		FioAppendDirectory(buf, MAX_PATH, sp, AI_DIR);
-		if (FileExists(buf)) this->ScanDir(buf);
+		if (FileExists(buf)) this->ScanDir(buf, false);
+		ttd_strlcat(buf, "library" PATHSEP, MAX_PATH);
+		if (FileExists(buf)) this->ScanDir(buf, true);
 	}
 }
 
@@ -116,6 +150,93 @@
 	delete this->engine;
 }
 
+bool AISquirrel::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm)
+{
+	AILibraryList::iterator iter = this->library_list.find(library);
+	/* Check if the library exists */
+	if (iter == this->library_list.end()) {
+		char error[1024];
+		snprintf(error, sizeof(error), "couldn't find library '%s'", library);
+		sq_throwerror(vm, OTTD2FS(error));
+		return false;
+	}
+
+	/* Check if the version matches */
+	if ((*iter).second->GetVersion() != version) {
+		char error[1024];
+		snprintf(error, sizeof(error), "this AI is expected library '%s' to be version %d, but it is version %d", library, version, (*iter).second->GetVersion());
+		sq_throwerror(vm, OTTD2FS(error));
+		return false;
+	}
+
+	/* Create a fake internal name */
+	char fake_class[1024];
+	snprintf(fake_class, sizeof(fake_class), "_internalNA%d", ++this->library_instance_count);
+
+	/* Load the library in a 'fake' namespace, so we can link it to the name the user requested */
+	sq_pushroottable(vm);
+	sq_pushstring(vm, OTTD2FS(fake_class), -1);
+	sq_newclass(vm, SQFalse);
+	/* Load the library */
+	if (!Squirrel::LoadScript(vm, (*iter).second->GetScriptName(), false)) {
+		char error[1024];
+		snprintf(error, sizeof(error), "there was a compile error when importing '%s'", library);
+		sq_throwerror(vm, OTTD2FS(error));
+		return false;
+	}
+	/* Create the fake class */
+	sq_newslot(vm, -3, SQFalse);
+	sq_pop(vm, 1);
+
+	/* Find the real class inside the fake class (like 'sets.Vector') */
+	sq_pushroottable(vm);
+	sq_pushstring(vm, OTTD2FS(fake_class), -1);
+	if (SQ_FAILED(sq_get(vm, -2))) {
+		sq_throwerror(vm, _SC("internal error assigning library class"));
+		return false;
+	}
+	sq_pushstring(vm, OTTD2FS((*iter).second->GetInstanceName()), -1);
+	if (SQ_FAILED(sq_get(vm, -2))) {
+		char error[1024];
+		snprintf(error, sizeof(error), "unable to find class '%s' in the library '%s'", (*iter).second->GetInstanceName(), library);
+		sq_throwerror(vm, OTTD2FS(error));
+		return false;
+	}
+	HSQOBJECT obj;
+	sq_getstackobj(vm, -1, &obj);
+	sq_pop(vm, 3);
+
+	/* Now link the name the user wanted to our 'fake' class */
+	sq_pushroottable(vm);
+	sq_pushstring(vm, OTTD2FS(class_name), -1);
+	sq_pushobject(vm, obj);
+	sq_newclass(vm, SQTrue);
+	sq_newslot(vm, -3, SQFalse);
+	sq_pop(vm, 1);
+
+	return true;
+}
+
+void AISquirrel::RegisterLibrary(AILibrary *library)
+{
+	const char *ai_name = library->GetDirName();
+
+	/* Check if we register twice; than the first always wins */
+	if (this->library_list.find(ai_name) != this->library_list.end()) {
+		/* In case they are not the same dir, give a warning */
+		if (strcasecmp(library->GetScriptName(), this->library_list[ai_name]->GetScriptName()) != 0) {
+			DEBUG(ai, 0, "Registering two Libraries with the same name");
+			DEBUG(ai, 0, "  1: %s", this->library_list[ai_name]->GetScriptName());
+			DEBUG(ai, 0, "  2: %s", library->GetScriptName());
+			DEBUG(ai, 0, "The first is taking precedence");
+		}
+		/* Delete the new AILibrary, as we will be using the old one */
+		delete library;
+		return;
+	}
+	this->library_list[ai_name] = library;
+}
+
 void AISquirrel::RegisterAI(AIInfo *info)
 {
 	const char *ai_name = info->GetDirName();
--- a/src/ai/ai_squirrel.hpp	Tue Jun 10 14:08:39 2008 +0000
+++ b/src/ai/ai_squirrel.hpp	Tue Jun 10 14:11:35 2008 +0000
@@ -14,6 +14,16 @@
 	~AISquirrel();
 
 	/**
+	 * Import a library inside the Squirrel VM.
+	 */
+	bool ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm);
+
+	/**
+	 * Register a library to be put in the available list.
+	 */
+	void RegisterLibrary(class AILibrary *library);
+
+	/**
 	 * Register an AI to be put in the available list.
 	 */
 	void RegisterAI(class AIInfo *info);
@@ -55,16 +65,23 @@
 
 private:
 	typedef std::map<const char *, class AIInfo *, ltstr> AIInfoList;
+	typedef std::map<const char *, class AILibrary *, ltstr> AILibraryList;
 
 	/**
-	 * Scan a dir for AIs. If found, AIInfo is created, and the AI is registered to the central system.
+	 * Scan a dir for AIs.
+	 *  For non-library-scan, if an AI is found, AIInfo is created, and the AI
+	 *    is registered to the central system.
+	 *  For library-scan, if a library is found, AILibrary is created, and the
+	 *    library is registered to the central system.
 	 */
-	void ScanDir(const char *dirname);
+	void ScanDir(const char *dirname, bool library_dir, char *library_depth = NULL);
 
 	AIInfoList info_list;
+	AILibraryList library_list;
 	class Squirrel *engine;
 	char *current_script;
 	char *current_dir;
+	int library_instance_count;
 };
 
 #endif /* AI_SQUIRREL_HPP */
--- a/src/ai/api/ai_controller.cpp	Tue Jun 10 14:08:39 2008 +0000
+++ b/src/ai/api/ai_controller.cpp	Tue Jun 10 14:11:35 2008 +0000
@@ -15,6 +15,7 @@
 #include "../../squirrel_std.hpp"
 #include "ai_controller.hpp"
 #include "../ai_threads.h"
+#include "../ai_info.hpp"
 
 /* Convert all AI related classes to Squirrel data.
  * Note: this line a marker in squirrel_export.sh. Do not change! */
@@ -72,6 +73,8 @@
 
 void AIController::RegisterClasses()
 {
+	this->engine->AddMethod("import", &AILibrary::Import, 4, "tssi");
+
 	/* Register all classes */
 	squirrel_register_std(this->engine);
 	SQAIAbstractList_Register(this->engine);