(svn r13512) [NoAI] -Fix: don't load a library over and over, but keep track of which libraries we have loaded (per AI) and re-use it where possible (reduces memory-footprint) noai
authortruebrain
Fri, 13 Jun 2008 20:19:00 +0000
branchnoai
changeset 10958 65088d587094
parent 10957 7a140b4cd91d
child 10971 aaf89f8c59b9
(svn r13512) [NoAI] -Fix: don't load a library over and over, but keep track of which libraries we have loaded (per AI) and re-use it where possible (reduces memory-footprint)
[NoAI] -Fix: change the fake-library-name-counter to a per AI value, not global
[NoAI] -Fix: Load the script inside the thread, not in the main thread. This avoids unneeded error-handling
src/ai/ai.cpp
src/ai/ai_squirrel.cpp
src/ai/ai_squirrel.hpp
src/ai/api/ai_controller.cpp
src/ai/api/ai_controller.hpp
--- a/src/ai/ai.cpp	Fri Jun 13 19:57:25 2008 +0000
+++ b/src/ai/ai.cpp	Fri Jun 13 20:19:00 2008 +0000
@@ -218,7 +218,7 @@
 
 bool AI_ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm)
 {
-	return _ai_squirrel->ImportLibrary(library, class_name, version, vm);
+	return _ai_squirrel->ImportLibrary(library, class_name, version, vm, _ai_player[_current_player]);
 }
 
 void AI_Rescan()
--- a/src/ai/ai_squirrel.cpp	Fri Jun 13 19:57:25 2008 +0000
+++ b/src/ai/ai_squirrel.cpp	Fri Jun 13 20:19:00 2008 +0000
@@ -17,6 +17,7 @@
 #include "../squirrel.hpp"
 #include "ai_info.hpp"
 #include "ai_squirrel.hpp"
+#include "api/ai_controller.hpp"
 
 void AISquirrel::ScanDir(const char *dirname, bool library_dir, char *library_depth)
 {
@@ -132,7 +133,6 @@
 
 AISquirrel::AISquirrel()
 {
-	this->library_instance_count = 0;
 	this->engine = new Squirrel();
 
 	/* Create the AIInfo class, and add the RegisterAI function */
@@ -162,7 +162,7 @@
 	delete this->engine;
 }
 
-bool AISquirrel::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm)
+bool AISquirrel::ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm, AIController *controller)
 {
 	AILibraryList::iterator iter = this->library_list.find(library);
 	/* Check if the library exists */
@@ -181,28 +181,35 @@
 		return false;
 	}
 
-	/* Create a fake internal name */
-	char fake_class[1024];
-	snprintf(fake_class, sizeof(fake_class), "_internalNA%d", ++this->library_instance_count);
-
 	/* Get the current table/class we belong to */
 	HSQOBJECT parent;
 	sq_getstackobj(vm, 1, &parent);
 
-	/* 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;
+	char fake_class[1024];
+	int next_number;
+	/* Check if we already have this library loaded.. if so, fill fake_class
+	 *  with the class-name it is nested in */
+	if (!controller->LoadedLibrary(library, &next_number, &fake_class[0], sizeof(fake_class))) {
+		/* Create a new fake internal name */
+		snprintf(fake_class, sizeof(fake_class), "_internalNA%d", next_number);
+
+		/* 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);
+
+		controller->AddLoadedLibrary(library, fake_class);
 	}
-	/* 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);
--- a/src/ai/ai_squirrel.hpp	Fri Jun 13 19:57:25 2008 +0000
+++ b/src/ai/ai_squirrel.hpp	Fri Jun 13 20:19:00 2008 +0000
@@ -6,7 +6,9 @@
 #define AI_SQUIRREL_HPP
 
 #include <map>
+#ifndef AI_CONTROLLER_HPP
 struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } };
+#endif /* AI_CONTROLLER_HPP */
 
 class AISquirrel {
 public:
@@ -16,7 +18,7 @@
 	/**
 	 * Import a library inside the Squirrel VM.
 	 */
-	bool ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm);
+	bool ImportLibrary(const char *library, const char *class_name, int version, HSQUIRRELVM vm, AIController *controller);
 
 	/**
 	 * Register a library to be put in the available list.
@@ -91,7 +93,6 @@
 	class Squirrel *engine;
 	char *current_script;
 	char *current_dir;
-	int library_instance_count;
 };
 
 #endif /* AI_SQUIRREL_HPP */
--- a/src/ai/api/ai_controller.cpp	Fri Jun 13 19:57:25 2008 +0000
+++ b/src/ai/api/ai_controller.cpp	Fri Jun 13 20:19:00 2008 +0000
@@ -6,6 +6,7 @@
 #include "../../openttd.h"
 #include "../../player_func.h"
 #include "../../core/alloc_func.hpp"
+#include "../../string_func.h"
 #include "table/strings.h"
 
 #include <squirrel.h>
@@ -152,32 +153,36 @@
 	AIController::Print(error_msg, FS2OTTD(message));
 }
 
-AIController::AIController(const char *script, const char *class_name)
+AIController::AIController(const char *script, const char *class_name) :
+	tick(0),
+	engine(NULL),
+	SQ_instance(NULL),
+	loaded_library_count(0)
 {
-	this->tick = 0;
-	this->SQ_instance = NULL;
+	this->script = strdup(script);
+	this->class_name = strdup(class_name);
+}
+
+void AIController::Start()
+{
+	assert(this->engine == NULL);
 
 	/* Create the Squirrel Engine and assign all required classes and settings */
 	this->engine = new Squirrel();
 	this->engine->SetPrintFunction(&PrintFunc);
 	this->RegisterClasses();
-	if (!this->engine->LoadScript(script)) {
+	if (!this->engine->LoadScript(this->script)) {
 		delete this->engine;
 		this->engine = NULL;
 		return;
 	}
 	/* Create the main-class */
 	this->SQ_instance = MallocT<SQObject>(sizeof(SQObject));
-	if (!this->engine->CreateClassInstance(class_name, this, this->SQ_instance)) {
+	if (!this->engine->CreateClassInstance(this->class_name, this, this->SQ_instance)) {
 		delete this->engine;
 		this->engine = NULL;
 		return;
 	}
-}
-
-void AIController::Start()
-{
-	if (this->engine == NULL) return;
 
 	/* Run the constructor first */
 	if (this->engine->MethodExists(*this->SQ_instance, "constructor")) {
@@ -191,6 +196,15 @@
 {
 	if (this->engine != NULL) delete this->engine;
 	free(this->SQ_instance);
+	free((void *)this->script);
+	free((void *)this->class_name);
+
+	for (LoadedLibraryList::iterator iter = this->loaded_library.begin(); iter != this->loaded_library.end(); iter++) {
+		free((void *)(*iter).second);
+		free((void *)(*iter).first);
+	}
+
+	this->loaded_library.clear();
 }
 
 void AIController::IncreaseTick()
@@ -202,3 +216,20 @@
 {
 	return this->tick;
 }
+
+bool AIController::LoadedLibrary(const char *library_name, int *next_number, char *fake_class_name, int fake_class_name_len)
+{
+	LoadedLibraryList::iterator iter = this->loaded_library.find(library_name);
+	if (iter == this->loaded_library.end()) {
+		*next_number = ++this->loaded_library_count;
+		return false;
+	}
+
+	ttd_strlcpy(fake_class_name, (*iter).second, fake_class_name_len);
+	return true;
+}
+
+void AIController::AddLoadedLibrary(const char *library_name, const char *fake_class_name)
+{
+	this->loaded_library[strdup(library_name)] = strdup(fake_class_name);
+}
--- a/src/ai/api/ai_controller.hpp	Fri Jun 13 19:57:25 2008 +0000
+++ b/src/ai/api/ai_controller.hpp	Fri Jun 13 20:19:00 2008 +0000
@@ -5,13 +5,17 @@
 #ifndef AI_CONTROLLER_HPP
 #define AI_CONTROLLER_HPP
 
+#include <map>
+#ifndef AI_SQUIRREL_HPP
+struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } };
+#endif /* AI_SQUIRREL_HPP */
+
 /**
  * The Controller, the class each AI should extend. It creates the AI, makes
  *  sure the logic kicks in correctly, and that GetTick() has a valid value.
  */
 class AIController {
-private:
-	uint tick;
+	friend class AISquirrel;
 
 public:
 	static const char *GetClassName() { return "AIController"; }
@@ -70,13 +74,37 @@
 	static void Print(bool error_msg, const char *message);
 
 private:
+	typedef std::map<const char *, const char *, ltstr> LoadedLibraryList;
+
+	uint tick;
 	class Squirrel *engine;
 	HSQOBJECT *SQ_instance;
+	LoadedLibraryList loaded_library;
+	int loaded_library_count;
+	const char *script;
+	const char *class_name;
 
 	/**
 	 * Register all classes that are known inside the NoAI API.
 	 */
 	void RegisterClasses();
+
+	/**
+	 * Check if a library is already loaded. If found, fake_class_name is filled
+	 *  with the fake class name as given via AddLoadedLibrary. If not found,
+	 *  next_number is set to the next number available for the fake namespace.
+	 * @param library_name The library to check if already loaded.
+	 * @param next_number The next available number for a library if not already loaded.
+	 * @param fake_class_name The name the library has if already loaded.
+	 * @param fake_class_name_len The maximum length of fake_class_name.
+	 * @return True if the library is already loaded.
+	 */
+	bool LoadedLibrary(const char *library_name, int *next_number, char *fake_class_name, int fake_class_name_len);
+
+	/**
+	 * Add a library as loaded.
+	 */
+	void AddLoadedLibrary(const char *library_name, const char *fake_class_name);
 };
 
 #endif /* AI_CONTROLLER_HPP */