(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
--- 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 */