(svn r9201) [NoAI] -Change: make adding a default-constructor for DefSQClass optional
-Add: added AIFactory code for squirrel. Now scripts can make theirself
known to the core in a simular way as in C++. Just make sure to add
your AIFactory instance to the GLOBAL space!
-Add: even more function in SquirrelEngine
--- a/bin/ai/SQNoAI/main.nut Thu Mar 15 13:28:11 2007 +0000
+++ b/bin/ai/SQNoAI/main.nut Thu Mar 15 13:36:45 2007 +0000
@@ -1,9 +1,16 @@
/* Extend our own AI from AIController so we can access basic things. */
class SQNoAI extends AIController {
- base = AIBase();
- company = AICompany();
- map = AIMap();
- town = AITown();
+ base = null;
+ company = null;
+ map = null;
+ town = null;
+
+ constructor() {
+ this.base = AIBase();
+ this.company = AICompany();
+ this.map = AIMap();
+ this.town = AITown();
+ }
function GameLoop();
}
@@ -42,5 +49,14 @@
}
}
-/* Tell OpenTTD we have an AI */
-RegisterAI("SQNoAI");
+class FSQNoAI extends AIFactory {
+ function GetAuthor() { return "OpenTTD Dev Team"; }
+ function GetName() { return "SQNoAI"; }
+ function GetDescription() { return "Rather simple AI that tests all the functions of the AI layer in Squirrel."; }
+ function GetVersion() { return 1; }
+ function GetDate() { return "2007-03-14"; }
+ function CreateInstance() { return "SQNoAI"; }
+}
+
+/* Tell the core we are an AI */
+iFSQNoAI <-FSQNoAI();
--- a/src/ai/core/ai_base.hpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/core/ai_base.hpp Thu Mar 15 13:36:45 2007 +0000
@@ -43,6 +43,7 @@
void SQAIBaseRegister(SquirrelEngine *engine) {
DefSQClass <AIBase> SQAIBase("AIBase");
SQAIBase.PreRegister(engine);
+ SQAIBase.AddConstructor(engine);
SQAIBase.DefSQFunction(engine, &AIBase::Random, "Random");
SQAIBase.DefSQFunction(engine, &AIBase::RandomRange, "RandomRange");
SQAIBase.DefSQFunction(engine, &AIBase::Chance, "Chance");
--- a/src/ai/core/ai_company.hpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/core/ai_company.hpp Thu Mar 15 13:36:45 2007 +0000
@@ -75,6 +75,7 @@
void SQAICompanyRegister(SquirrelEngine *engine) {
DefSQClass <AICompany> SQAICompany("AICompany");
SQAICompany.PreRegister(engine);
+ SQAICompany.AddConstructor(engine);
SQAICompany.DefSQFunction(engine, &AICompany::SetCompanyName, "SetCompanyName");
SQAICompany.DefSQFunction(engine, &AICompany::GetCompanyName, "GetCompanyName");
SQAICompany.DefSQFunction(engine, &AICompany::GetCompanyValue, "GetCompanyValue");
--- a/src/ai/core/ai_controller.hpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/core/ai_controller.hpp Thu Mar 15 13:36:45 2007 +0000
@@ -35,3 +35,12 @@
};
#endif /* AI_CONTROLLER_HPP */
+
+#ifdef SQUIRREL_CLASS
+void SQAIControllerRegister(SquirrelEngine *engine) {
+ DefSQClass <AIControllerSquirrel> SQAIController("AIController");
+ SQAIController.PreRegister(engine);
+ SQAIController.DefSQFunction(engine, &AIControllerSquirrel::GetTick, "GetTick");
+ SQAIController.PostRegister(engine);
+}
+#endif /* SQUIRREL_CLASS */
--- a/src/ai/core/ai_map.hpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/core/ai_map.hpp Thu Mar 15 13:36:45 2007 +0000
@@ -60,6 +60,7 @@
void SQAIMapRegister(SquirrelEngine *engine) {
DefSQClass <AIMap> SQAIMap("AIMap");
SQAIMap.PreRegister(engine);
+ SQAIMap.AddConstructor(engine);
SQAIMap.DefSQFunction(engine, &AIMap::IsValidTile, "IsValidTile");
SQAIMap.DefSQFunction(engine, &AIMap::GetMapSize, "GetMapSize");
SQAIMap.DefSQFunction(engine, &AIMap::GetMapSizeX, "GetMapSizeX");
--- a/src/ai/core/ai_town.hpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/core/ai_town.hpp Thu Mar 15 13:36:45 2007 +0000
@@ -62,6 +62,7 @@
void SQAITownRegister(SquirrelEngine *engine) {
DefSQClass <AITown> SQAITown("AITown");
SQAITown.PreRegister(engine);
+ SQAITown.AddConstructor(engine);
SQAITown.DefSQFunction(engine, &AITown::GetMaxTownID, "GetMaxTownID");
SQAITown.DefSQFunction(engine, &AITown::GetTownCount, "GetTownCount");
SQAITown.DefSQFunction(engine, &AITown::IsValidTown, "IsValidTown");
--- a/src/ai/squirrel/engine.cpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/squirrel/engine.cpp Thu Mar 15 13:36:45 2007 +0000
@@ -94,7 +94,22 @@
sq_createslot(vm, -3);
}
-void SquirrelEngine::CallMethod(HSQOBJECT instance, const char *method_name)
+bool SquirrelEngine::MethodExists(HSQOBJECT instance, const char *method_name)
+{
+ int top = sq_gettop(this->vm);
+ /* Go to the instance-root */
+ sq_pushobject(this->vm, instance);
+ /* Find the function-name inside the script */
+ sq_pushstring(this->vm, method_name, -1);
+ if (SQ_FAILED(sq_get(this->vm, -2))) {
+ sq_settop(this->vm, top);
+ return false;
+ }
+ sq_settop(this->vm, top);
+ return true;
+}
+
+void SquirrelEngine::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret)
{
/* Store the current top */
int top = sq_gettop(this->vm);
@@ -109,7 +124,8 @@
}
/* Call the method */
sq_pushobject(this->vm, instance);
- sq_call(this->vm, 1, 0, 0);
+ sq_call(this->vm, 1, ret == NULL ? SQFalse : SQTrue, SQFalse);
+ if (ret != NULL) sq_getstackobj(vm, -1, ret);
/* Reset the top */
sq_settop(this->vm, top);
}
--- a/src/ai/squirrel/engine.hpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/squirrel/engine.hpp Thu Mar 15 13:36:45 2007 +0000
@@ -62,9 +62,17 @@
void AddClassEnd();
/**
- * Call a method of an instance.
+ * Call a method of an instance, in various flavors.
*/
- void CallMethod(HSQOBJECT instance, const char *method_name);
+ void CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret);
+ void CallMethod(HSQOBJECT instance, const char *method_name) { this->CallMethod(instance, method_name, NULL); }
+ const char *CallStringMethod(HSQOBJECT instance, const char *method_name) { HSQOBJECT ret; this->CallMethod(instance, method_name, &ret); return ObjectToString(&ret); }
+ int CallIntegerMethod(HSQOBJECT instance, const char *method_name) { HSQOBJECT ret; this->CallMethod(instance, method_name, &ret); return ObjectToInteger(&ret); }
+
+ /**
+ * Check if a method exists in an instance.
+ */
+ bool MethodExists(HSQOBJECT instance, const char *method_name);
/**
* Creates a class instance.
@@ -80,12 +88,40 @@
* @note This will only work just after a function-call from within Squirrel
* to your C++ function.
*/
- static bool GetInstance(HSQUIRRELVM vm, SQUserPointer *ptr) { return SQ_SUCCEEDED(sq_getinstanceup(vm, 1, ptr, 0)); }
+ static bool GetRealInstance(HSQUIRRELVM vm, SQUserPointer *ptr) { return SQ_SUCCEEDED(sq_getinstanceup(vm, 1, ptr, 0)); }
- HSQUIRRELVM GetVM() {
-#warning Please stop using this function ASAP
- return this->vm;
- }
+ /**
+ * Get the Squirrel-instance pointer.
+ * @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); return true; }
+
+ /**
+ * Convert a Squirrel-object to a string.
+ */
+ static const char *ObjectToString(HSQOBJECT *ptr) { return sq_objtostring(ptr); }
+
+ /**
+ * Convert a Squirrel-object to an integer.
+ */
+ static int ObjectToInteger(HSQOBJECT *ptr) { return sq_objtointeger(ptr); }
+
+ /**
+ * Sets a pointer in the VM that is reasable from where ever you are, what
+ * ever your state is. Useful to keep track of the main instance.
+ */
+ void SetGlobalPointer(void *ptr) { sq_setforeignptr(this->vm, ptr); }
+
+ /**
+ * Get the pointer as set by SetGlobalPointer.
+ */
+ static void *GetGlobalPointer(HSQUIRRELVM vm) { return sq_getforeignptr(vm); }
+
+ /**
+ * Throw a Squirrel error that will be nicely displayed to the user.
+ */
+ void ThrowError(const char *error) { sq_throwerror(this->vm, error); }
};
#endif /* SQUIRREL_CORE_HPP */
--- a/src/ai/squirrel/squirrel.cpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/squirrel/squirrel.cpp Thu Mar 15 13:36:45 2007 +0000
@@ -3,8 +3,6 @@
/** @file squirrel.cpp allows loading squirrel scripts to control an AI */
#include "squirrel.hpp"
-#include <sqstdio.h>
-#include <sqstdaux.h>
#include "../../debug.h"
#define SQUIRREL_CLASS
@@ -12,109 +10,134 @@
#include "squirrel_class.hpp"
#include "../core/ai_base.hpp"
#include "../core/ai_company.hpp"
+#include "../core/ai_controller.hpp"
#include "../core/ai_map.hpp"
#include "../core/ai_town.hpp"
-SQInteger Squirrel::SQDestructor(SQUserPointer p, SQInteger size)
-{
- return 1;
-}
-
-SQInteger Squirrel::SQConstructor(HSQUIRRELVM vm)
-{
- DEBUG(ai, 0, "[squirrel] Please do NOT define your AI class yourself; use RegisterAI() instead");
- return SQ_ERROR;
-}
+static FSquirrel iFSquirrel; ///< Tell the AI-core that we have an AI with which we like to play.
-SQInteger Squirrel::SQGetTick(HSQUIRRELVM vm)
-{
- SQUserPointer self;
+/**
+ * Our tiny wrapper between C++ and Squirrel.
+ */
+class AIFactorySquirrel: public AIFactoryBase {
+public:
+ SquirrelEngine *engine;
+ HSQOBJECT SQ_instance;
- if (!SquirrelEngine::GetInstance(vm, &self)) {
- DEBUG(ai, 0, "[squirrel] Failed to find instance pointer");
- return SQ_ERROR;
+ const char *GetAuthor() { return this->engine->CallStringMethod (this->SQ_instance, "GetAuthor"); }
+ const char *GetName() { return this->engine->CallStringMethod (this->SQ_instance, "GetName"); }
+ const char *GetDescription() { return this->engine->CallStringMethod (this->SQ_instance, "GetDescription"); }
+ int GetVersion() { return this->engine->CallIntegerMethod(this->SQ_instance, "GetVersion"); }
+ const char *GetDate() { return this->engine->CallStringMethod (this->SQ_instance, "GetDate"); }
+ AIController *CreateInstance();
+
+ /**
+ * Register Squirrel script to the Factory.
+ */
+ void RegisterSquirrel() { this->RegisterFactory(this->GetName()); }
+
+ /**
+ * Check if a method exists in the script.
+ */
+ void CheckMethods(AIFactorySquirrel *fbase, SQInteger *res, const char *name);
+};
+
+void AIFactorySquirrel::CheckMethods(AIFactorySquirrel *fbase, SQInteger *res, const char *name)
+{
+ if (!fbase->engine->MethodExists(fbase->SQ_instance, name)) {
+ char error[1024];
+ snprintf(error, sizeof(error), "Missing method '%s' for FactoryClass", name);
+ fbase->engine->ThrowError(error);
+ *res = SQ_ERROR;
}
-
- /* Return the value */
- sq_pushinteger(vm, ((Squirrel *)self)->GetTick());
- return 1;
}
-SQInteger Squirrel::SQRegisterAI(HSQUIRRELVM vm)
+SQInteger FSquirrel::FactoryConstructor(HSQUIRRELVM vm)
{
- const SQChar *classname = NULL;
- sq_getstring(vm, -1, &classname);
-
- /* Check if the class really exists */
- int oldtop = sq_gettop(vm);
- sq_pushroottable(vm);
- sq_pushstring(vm, classname, -1);
- if (SQ_FAILED(sq_get(vm, -2))) {
- DEBUG(ai, 0, "[squirrel] Failed to find class '%s'", classname);
- return SQ_ERROR;
- }
- sq_settop(vm, oldtop);
+ AIFactorySquirrel *fbase = new AIFactorySquirrel();
+ SQInteger res = 0;
- /* Retreive the 'this' pointer from the global space */
- Squirrel *self = (Squirrel *)sq_getforeignptr(vm);
- if (self == NULL) {
- DEBUG(ai, 0, "[squirrel] Currently only one RegisterAI call is allowed!");
- return SQ_ERROR;
- }
- /* Make sure we only register one AI in this script */
- sq_setforeignptr(vm, NULL);
+ SquirrelEngine::GetInstance(vm, &fbase->SQ_instance);
+ fbase->engine = (SquirrelEngine *)SquirrelEngine::GetGlobalPointer(vm);
- /* Create the new class */
- if (!self->engine->CreateClassInstance(classname, self, &self->SQ_instance)) {
- return SQ_ERROR;
- }
+ /* Check if all needed fields are there */
+ fbase->CheckMethods(fbase, &res, "GetAuthor");
+ fbase->CheckMethods(fbase, &res, "GetName");
+ fbase->CheckMethods(fbase, &res, "GetDescription");
+ fbase->CheckMethods(fbase, &res, "GetVersion");
+ fbase->CheckMethods(fbase, &res, "GetDate");
+ fbase->CheckMethods(fbase, &res, "CreateInstance");
- return 0;
+ /* Only register if all methods were known */
+ if (res == 0) fbase->RegisterSquirrel();
+
+ return res;
}
-void Squirrel::RegisterClasses()
+void FSquirrel::Initializer()
{
- HSQUIRRELVM vm = this->engine->GetVM();
- SQInteger top = sq_gettop(vm);
+ this->engine = new SquirrelEngine();
- /* Store 'this' so we can find it back later on */
- sq_setforeignptr(vm, this);
-
- this->engine->AddClassBegin("AIController");
-
- /* Create a constructor, so we can assign the right 'this' */
- this->engine->AddMethod("constructor", &Squirrel::SQConstructor, 1, "x");
- /* Assign other functions as AIController won't be done via the normal way */
- this->engine->AddMethod("GetTick", &Squirrel::SQGetTick, 1, "x");
-
+ /* Create the AIFactory class, and bind the constructor */
+ this->engine->AddClassBegin("AIFactory");
+ this->engine->AddMethod("constructor", &FSquirrel::FactoryConstructor, 1, "x");
this->engine->AddClassEnd();
- /* Register the 'RegisterAI()' function */
- sq_pushroottable(vm);
- this->engine->AddMethod("RegisterAI", &Squirrel::SQRegisterAI, 2, "ts");
+ /* Set a dummy AIController, so script can load */
+ this->engine->AddClassBegin("AIController");
+ this->engine->AddClassEnd();
+ /* Mark the engine as global pointer */
+ this->engine->SetGlobalPointer(this->engine);
+ /* Now load all scripts we can find */
+ this->engine->LoadScript("ai/SQNoAI/main.nut");
+}
+
+FSquirrel::~FSquirrel()
+{
+ delete this->engine;
+}
+
+AIController *AIFactorySquirrel::CreateInstance()
+{
+ const char *class_name = this->engine->CallStringMethod(this->SQ_instance, "CreateInstance");
+ AIControllerSquirrel *controller = new AIControllerSquirrel("ai/SQNoAI/main.nut", class_name);
+
+ return controller;
+}
+
+void AIControllerSquirrel::RegisterClasses()
+{
+ /* Ignore AIFactory if we are really starting an AI */
+ this->engine->AddClassBegin("AIFactory");
+ this->engine->AddClassEnd();
+
+ /* Register all classes */
+ SQAIControllerRegister(this->engine);
SQAIBaseRegister(this->engine);
SQAICompanyRegister(this->engine);
SQAIMapRegister(this->engine);
SQAITownRegister(this->engine);
- /* Reset top */
- sq_settop(vm, top);
+ this->engine->SetGlobalPointer(this->engine);
}
-Squirrel::Squirrel(const char *script)
+AIControllerSquirrel::AIControllerSquirrel(const char *script, const char *class_name)
{
this->engine = new SquirrelEngine();
this->RegisterClasses();
this->engine->LoadScript(script);
+ /* Create the main-class */
+ this->engine->CreateClassInstance(class_name, this, &this->SQ_instance);
+ this->engine->CallMethod(this->SQ_instance, "constructor");
}
-Squirrel::~Squirrel()
+AIControllerSquirrel::~AIControllerSquirrel()
{
delete this->engine;
}
-/* virtual */ void Squirrel::GameLoop()
+/* virtual */ void AIControllerSquirrel::GameLoop()
{
this->engine->CallMethod(this->SQ_instance, "GameLoop");
}
--- a/src/ai/squirrel/squirrel.hpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/squirrel/squirrel.hpp Thu Mar 15 13:36:45 2007 +0000
@@ -5,7 +5,6 @@
#ifndef SQUIRREL_HPP
#define SQUIRREL_HPP
-#include "../core/ai_controller.hpp"
#ifdef _UNICODE
/* Disable unicode for squirrel to allow compilation with MINGW
* and simplify coding for WIN32 (squirrel headers miss a lot of "string" functions)
@@ -13,50 +12,46 @@
#undef _UNICODE
#endif
#include <squirrel.h>
+#include "../core/ai_controller.hpp"
+#include "../core/ai_factory.hpp"
#include "engine.hpp"
-class Squirrel: public AIController {
+class AIControllerSquirrel: public AIController {
private:
- HSQOBJECT SQ_instance; ///< The internal instance of squirrel
SquirrelEngine *engine; ///< The SquirrelEngine
+ HSQOBJECT SQ_instance; ///< The internal instance of squirrel
/**
* Registers all our classes, so it can be used from Squirrel.
*/
void RegisterClasses();
- /**
- * Constructor for Squirrel AIController. We assign the real 'this' value to
- * the instance pointer, so we can easy look up this->.
- */
- static SQInteger SQConstructor(HSQUIRRELVM vm);
-
- /**
- * Destructor for Squirrel AIController. This is called if the class is
- * destroyed inside the script.
- */
- static SQInteger SQDestructor(SQUserPointer p, SQInteger size);
+public:
+ AIControllerSquirrel(const char *script_dir, const char *class_name);
+ ~AIControllerSquirrel();
- /**
- * A wrapper around the AIController class, as that can't be automated.
- */
- static SQInteger SQGetTick(HSQUIRRELVM vm);
+ /* virtual */ void GameLoop();
- /**
- * This is called by the init-part of a script to indicate there is an AI class.
- */
- static SQInteger SQRegisterAI(HSQUIRRELVM vm);
+ uint GetTick() { return AIController::GetTick(); }
+};
- /**
- * Create a class with the name 'class' and set all pointers correctly.
- */
- bool SQCreateClass(const char *classname);
+class FSquirrel: public AIFactory<FSquirrel> {
+private:
+ SquirrelEngine *engine; ///< The SquirrelEngine
+
+ static SQInteger FactoryConstructor(HSQUIRRELVM vm);
public:
- Squirrel(const char *script_dir);
- ~Squirrel();
+ ~FSquirrel();
- /* virtual */ void GameLoop();
+ const char *GetAuthor() { return "OpenTTD Dev Team"; }
+ const char *GetName() { return "Squirrel"; }
+ const char *GetDescription() { return "Squirrel Module for loading scripts"; }
+ int GetVersion() { return 0; }
+ const char *GetDate() { return ""; }
+ AIController *CreateInstance() { return NULL; }
+ bool AllowStartup() { return false; }
+ void Initializer();
};
#endif /* SQUIRREL_HPP */
--- a/src/ai/squirrel/squirrel_class.hpp Thu Mar 15 13:28:11 2007 +0000
+++ b/src/ai/squirrel/squirrel_class.hpp Thu Mar 15 13:36:45 2007 +0000
@@ -54,6 +54,10 @@
void PreRegister(SquirrelEngine *engine)
{
engine->AddClassBegin(this->classname);
+ }
+
+ void AddConstructor(SquirrelEngine *engine)
+ {
engine->AddMethod("constructor", &DefSQClass::Constructor);
}