(svn r9185) [NoAI] -Codechange: split up the squirrel code so we have SquirrelCore noai
authortruelight
Thu, 15 Mar 2007 00:01:33 +0000
branchnoai
changeset 9393 04bd925b9069
parent 9392 7791ceeea57b
child 9394 796c684ca5b6
(svn r9185) [NoAI] -Codechange: split up the squirrel code so we have SquirrelCore
and the rest. The first isn't depending of any AI code. First step in
generalizing squirrel-code.
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/ai/squirrel/core.cpp
src/ai/squirrel/core.hpp
src/ai/squirrel/squirrel.cpp
src/ai/squirrel/squirrel.hpp
--- a/projects/openttd.vcproj	Wed Mar 14 23:01:30 2007 +0000
+++ b/projects/openttd.vcproj	Thu Mar 15 00:01:33 2007 +0000
@@ -960,6 +960,12 @@
 				RelativePath=".\..\src\ai\squirrel\convert.hpp">
 			</File>
 			<File
+				RelativePath=".\..\src\ai\squirrel\core.cpp">
+			</File>
+			<File
+				RelativePath=".\..\src\ai\squirrel\core.hpp">
+			</File>
+			<File
 				RelativePath=".\..\src\ai\squirrel\squirrel.cpp">
 			</File>
 			<File
--- a/projects/openttd_vs80.vcproj	Wed Mar 14 23:01:30 2007 +0000
+++ b/projects/openttd_vs80.vcproj	Thu Mar 15 00:01:33 2007 +0000
@@ -1500,6 +1500,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\ai\squirrel\core.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\ai\squirrel\core.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ai\squirrel\squirrel.cpp"
 				>
 			</File>
--- a/source.list	Wed Mar 14 23:01:30 2007 +0000
+++ b/source.list	Thu Mar 15 00:01:33 2007 +0000
@@ -291,6 +291,8 @@
 ai/NoAI/NoAI.hpp
 ai/squirrel/convert.cpp
 ai/squirrel/convert.hpp
+ai/squirrel/core.cpp
+ai/squirrel/core.hpp
 ai/squirrel/squirrel.cpp
 ai/squirrel/squirrel.hpp
 ai/squirrel/squirrel_class.hpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/squirrel/core.cpp	Thu Mar 15 00:01:33 2007 +0000
@@ -0,0 +1,171 @@
+/* $Id$ */
+
+/** @file core.hpp the implementation of the SquirrelCore class. It handles all Squirrel-stuff and gives a nice API back to work with. */
+
+#ifdef _UNICODE
+/* Disable unicode for squirrel to allow compilation with MINGW
+ * and simplify coding for WIN32 (squirrel headers miss a lot of "string" functions)
+ */
+#undef _UNICODE
+#endif
+#include <squirrel.h>
+#include <sqstdio.h>
+#include <stdarg.h>
+#include "../../stdafx.h"
+#include "../../debug.h"
+#include "core.hpp"
+
+void SquirrelCore::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column)
+{
+#ifdef _SQ64
+	printf("Error %s:%ld/%ld: %s\n", source, line, column, desc);
+#else
+	printf("Error %s:%d/%d: %s\n", source, line, column, desc);
+#endif
+}
+
+void SquirrelCore::RunError(HSQUIRRELVM vm, const char *error)
+{
+	printf("%s\n", error);
+}
+
+SQInteger SquirrelCore::_RunError(HSQUIRRELVM vm)
+{
+	const SQChar *sErr = 0;
+
+	if (sq_gettop(vm) >= 1) {
+		if (SQ_SUCCEEDED(sq_getstring(vm, -1, &sErr))) {
+			SquirrelCore::RunError(vm, sErr);
+		}
+	}
+
+	SquirrelCore::RunError(vm, "Unknown error");
+	return 0;
+}
+
+void SquirrelCore::PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
+{
+	va_list arglist;
+	va_start(arglist, s);
+	vprintf(s, arglist);
+	va_end(arglist);
+	printf("\n");
+}
+
+void SquirrelCore::AddMethod(const char *method_name, SQFUNCTION proc, uint nparam, const char *params)
+{
+	sq_pushstring(this->vm, method_name, -1);
+	sq_newclosure(this->vm, proc, 0);
+	sq_setparamscheck(this->vm, nparam, params);
+	sq_setnativeclosurename(this->vm, -1, method_name);
+	sq_createslot(this->vm, -3);
+}
+
+void SquirrelCore::AddMethod(const char *method_name, SQFUNCTION proc)
+{
+	sq_pushstring(this->vm, method_name, -1);
+	sq_newclosure(this->vm, proc, 0);
+	sq_setnativeclosurename(this->vm, -1, method_name);
+	sq_createslot(this->vm, -3);
+}
+
+void SquirrelCore::AddClassBegin(const char *class_name)
+{
+	sq_pushroottable(this->vm);
+	sq_pushstring(this->vm, class_name, -1);
+	sq_newclass(this->vm, SQFalse);
+}
+
+void SquirrelCore::AddClassEnd()
+{
+	sq_createslot(vm, -3);
+}
+
+void SquirrelCore::CallMethod(HSQOBJECT instance, const char *method_name)
+{
+	/* Store the current top */
+	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))) {
+		DEBUG(misc, 0, "[squirrel] Could not find '%s' in the class", method_name);
+		sq_settop(this->vm, top);
+		return;
+	}
+	/* Call the method */
+	sq_pushobject(this->vm, instance);
+	sq_call(this->vm, 1, 0, 0);
+	/* Reset the top */
+	sq_settop(this->vm, top);
+}
+
+bool SquirrelCore::CreateClassInstance(const char *class_name, void *real_instance, HSQOBJECT *instance)
+{
+	int oldtop = sq_gettop(this->vm);
+
+	/* First, find the class */
+	sq_pushroottable(this->vm);
+	sq_pushstring(this->vm, class_name, -1);
+	if (SQ_FAILED(sq_get(this->vm, -2))) {
+		DEBUG(misc, 0, "[squirrel] Failed to find class by the name '%s'", class_name);
+		sq_settop(this->vm, oldtop);
+		return false;
+	}
+
+	/* Create the instance */
+	if (SQ_FAILED(sq_createinstance(this->vm, -1))) {
+		DEBUG(misc, 0, "[squirrel] Failed to create instance for class '%s'", class_name);
+		sq_settop(this->vm, oldtop);
+		return false;
+	}
+
+	if (instance != NULL) {
+		/* Find our instance */
+		sq_getstackobj(this->vm, -1, instance);
+		/* Add a reference to it, so it survives for ever */
+		sq_addref(this->vm, instance);
+	}
+	/* Store it in the class */
+	sq_setinstanceup(this->vm, -1, real_instance);
+
+	/* Reset the top */
+	sq_settop(this->vm, oldtop);
+
+	return true;
+}
+
+SquirrelCore::SquirrelCore()
+{
+	this->vm = sq_open(1024);
+
+	/* Handle compile-errors ourself, so we can display it nicely */
+	sq_setcompilererrorhandler(this->vm, &SquirrelCore::CompileError);
+	sq_notifyallexceptions(this->vm, SQTrue);
+	/* Set a good print-function */
+	sq_setprintfunc(this->vm, &SquirrelCore::PrintFunc);
+	/* Handle runtime-errors ourself, so we can display it nicely */
+	sq_newclosure(this->vm, &SquirrelCore::_RunError, 0);
+	sq_seterrorhandler(this->vm);
+
+	sq_pushroottable(this->vm);
+}
+
+bool SquirrelCore::LoadScript(const char *script)
+{
+	/* Load and run the script */
+	if (SQ_FAILED(sqstd_dofile(this->vm, script, SQFalse, SQTrue))) {
+		DEBUG(misc, 0, "[squirrel] Failed to compile '%s'", script);
+		return false;
+	}
+
+	return true;
+}
+
+SquirrelCore::~SquirrelCore()
+{
+	/* Clean up the stuff */
+	sq_pop(this->vm, 1);
+	sq_close(this->vm);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ai/squirrel/core.hpp	Thu Mar 15 00:01:33 2007 +0000
@@ -0,0 +1,81 @@
+/* $Id$ */
+
+/** @file core.hpp defines the SquirrelCore class */
+
+#ifndef SQUIRREL_CORE_HPP
+#define SQUIRREL_CORE_HPP
+
+class SquirrelCore {
+private:
+	HSQUIRRELVM vm;        ///< The VirtualMachine instnace for squirrel
+
+	/**
+	 * The internal RunError handler. It looks up the real error and calls RunError with it.
+	 */
+	static SQInteger _RunError(HSQUIRRELVM vm);
+
+protected:
+	/**
+	 * The CompileError handler.
+	 */
+	static void CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column);
+
+	/**
+	 * The RunError handler.
+	 */
+	static void RunError(HSQUIRRELVM vm, const char *error);
+
+	/**
+	 * If a user runs 'print' inside a script, this function gets the params.
+	 */
+	static void PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...);
+
+public:
+	SquirrelCore();
+	~SquirrelCore();
+
+	/**
+	 * Load a script.
+	 * @param script The full script-name to load.
+	 * @return False if loading failed.
+	 */
+	bool LoadScript(const char *script);
+
+	/**
+	 * Adds a function to the stack. Depending on the current state this means
+	 *  either a method or a global function.
+	 */
+	void AddMethod(const char *method_name, SQFUNCTION proc, uint nparam, const char *params);
+	void AddMethod(const char *method_name, SQFUNCTION proc);
+
+	/**
+	 * Adds a class to the global scope. Make sure to call AddClassEnd when you
+	 *  are done adding methods.
+	 */
+	void AddClassBegin(const char *class_name);
+
+	/**
+	 * Finishes adding a class to the global scope. If this isn't called, no
+	 *  class is really created.
+	 */
+	void AddClassEnd();
+
+	/**
+	 * Call a method of an instance.
+	 */
+	void CallMethod(HSQOBJECT instance, const char *method_name);
+
+	/**
+	 * Creates a class instance.
+	 * @param class_name The name of the class of which we create an instance.
+	 * @param real_instance The instance to the real class, if it represents a real class.
+	 * @param instance Returning value with the pointer to the instance.
+	 * @return False if creating failed.
+	 */
+	bool CreateClassInstance(const char *class_name, void *real_instance, HSQOBJECT *instance);
+
+	HSQUIRRELVM GetVM() { return this->vm; }
+	static bool GetInstance(HSQUIRRELVM vm, SQUserPointer *ptr) { if (SQ_FAILED(sq_getinstanceup(vm, 1, ptr, 0))) return false; return true; }
+};
+
+#endif /* SQUIRREL_CORE_HPP */
--- a/src/ai/squirrel/squirrel.cpp	Wed Mar 14 23:01:30 2007 +0000
+++ b/src/ai/squirrel/squirrel.cpp	Thu Mar 15 00:01:33 2007 +0000
@@ -5,7 +5,6 @@
 #include "squirrel.hpp"
 #include <sqstdio.h>
 #include <sqstdaux.h>
-#include <stdarg.h>
 #include "../../debug.h"
 
 #define SQUIRREL_CLASS
@@ -16,48 +15,6 @@
 #include "../core/ai_map.hpp"
 #include "../core/ai_town.hpp"
 
-/**
- * Here we handle errors that come from squirrel.
- */
-static void Squirrel_CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column)
-{
-#ifdef _SQ64
-	printf("Error %s:%ld/%ld: %s\n", source, line, column, desc);
-#else
-	printf("Error %s:%d/%d: %s\n", source, line, column, desc);
-#endif
-}
-
-/**
- * Here we handle run-time errors that come from squirrel.
- */
-static SQInteger Squirrel_RunError(HSQUIRRELVM vm)
-{
-	const SQChar *sErr = 0;
-
-	if (sq_gettop(vm) >= 1) {
-		if (SQ_SUCCEEDED(sq_getstring(vm, -1, &sErr))) {
-			printf("%s\n", sErr);
-		} else {
-			printf("Unknown error\n");
-		}
-	}
-
-	return 0;
-}
-
-/**
- * Here we handle 'print' command within squirrel script.
- */
-static void Squirrel_PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
-{
-	va_list arglist;
-	va_start(arglist, s);
-	vprintf(s, arglist);
-	va_end(arglist);
-	printf("\n");
-}
-
 SQInteger Squirrel::SQDestructor(SQUserPointer p, SQInteger size)
 {
 	return 1;
@@ -71,52 +28,18 @@
 
 SQInteger Squirrel::SQGetTick(HSQUIRRELVM vm)
 {
-	SQUserPointer ptr;
+	SQUserPointer self;
 
-	/* Find the 'self' pointer */
-	if (SQ_FAILED(sq_getinstanceup(vm, 1, &ptr, 0))) {
-		DEBUG(ai, 0, "[squirrel] Failed to find the instance-pointer");
+	if (!SquirrelCore::GetInstance(vm, &self)) {
+		DEBUG(ai, 0, "[squirrel] Failed to find instance pointer");
 		return SQ_ERROR;
 	}
 
 	/* Return the value */
-	sq_pushinteger(vm, ((Squirrel *)ptr)->GetTick());
+	sq_pushinteger(vm, ((Squirrel *)self)->GetTick());
 	return 1;
 }
 
-bool Squirrel::SQCreateClass(const char *classname)
-{
-	int oldtop = sq_gettop(this->vm);
-
-	/* First, find the class */
-	sq_pushroottable(this->vm);
-	sq_pushstring(this->vm, classname, -1);
-	sq_get(this->vm, -2);
-
-	/* Create the instance */
-	if (SQ_FAILED(sq_createinstance(this->vm, -1))) {
-		DEBUG(ai, 0, "[squirrel] Failed to create instance for class '%s'", classname);
-		sq_settop(this->vm, oldtop);
-		return false;
-	}
-
-	/* Find our instance */
-	sq_getstackobj(this->vm, -1, &this->SQ_instance);
-	/* Add a reference to it, so it survives for ever */
-	sq_addref(this->vm, &this->SQ_instance);
-	/* Store it in the class */
-	sq_setinstanceup(this->vm, -1, this);
-	/* Make sure we get called when the AI is destroyed */
-	sq_setreleasehook(this->vm, -1, &Squirrel::SQDestructor);
-
-	/* Reset the top */
-	sq_settop(this->vm, oldtop);
-
-	DEBUG(ai, 1, "[squirrel] Created an instance of class '%s' with pointer '%p'", classname, this->SQ_instance._unVal.pUserPointer);
-
-	return true;
-}
-
 SQInteger Squirrel::SQRegisterAI(HSQUIRRELVM vm)
 {
 	const SQChar *classname = NULL;
@@ -142,111 +65,56 @@
 	sq_setforeignptr(vm, NULL);
 
 	/* Create the new class */
-	if (!self->SQCreateClass(classname)) {
+	if (!self->core->CreateClassInstance(classname, self, &self->SQ_instance)) {
 		return SQ_ERROR;
 	}
 
 	return 0;
 }
 
-inline void Squirrel::SQAddMethod(const char *name, SQFUNCTION proc, uint nparam, const char *params)
-{
-	sq_pushstring(this->vm, name, -1);
-	sq_newclosure(this->vm, proc, 0);
-	sq_setparamscheck(this->vm, nparam, params);
-	sq_setnativeclosurename(this->vm, -1, name);
-	sq_createslot(this->vm, -3);
-}
-
 void Squirrel::RegisterClasses()
 {
-	SQInteger top = sq_gettop(this->vm);
+	HSQUIRRELVM vm = this->core->GetVM();
+	SQInteger top = sq_gettop(vm);
 
 	/* Store 'this' so we can find it back later on */
-	sq_setforeignptr(this->vm, this);
+	sq_setforeignptr(vm, this);
 
-	/* Create AIController class (the 'main' of it all) */
-	sq_pushroottable(this->vm);
-	sq_pushstring(this->vm, "AIController", -1);
-	sq_newclass(this->vm, SQFalse);
+	this->core->AddClassBegin("AIController");
 
 	/* Create a constructor, so we can assign the right 'this' */
-	this->SQAddMethod("constructor", &Squirrel::SQConstructor, 1, "x");
+	this->core->AddMethod("constructor", &Squirrel::SQConstructor, 1, "x");
 	/* Assign other functions as AIController won't be done via the normal way */
-	this->SQAddMethod("GetTick", &Squirrel::SQGetTick, 1, "x");
+	this->core->AddMethod("GetTick", &Squirrel::SQGetTick, 1, "x");
 
-	/* Finally really create the class */
-	sq_createslot(this->vm, -3);
+	this->core->AddClassEnd();
 
 	/* Register the 'RegisterAI()' function */
-	sq_pushroottable(this->vm);
-	this->SQAddMethod("RegisterAI", &Squirrel::SQRegisterAI, 2, "ts");
+	sq_pushroottable(vm);
+	this->core->AddMethod("RegisterAI", &Squirrel::SQRegisterAI, 2, "ts");
 
-	SQAIBaseRegister(this->vm);
-	SQAICompanyRegister(this->vm);
-	SQAIMapRegister(this->vm);
-	SQAITownRegister(this->vm);
+	SQAIBaseRegister(vm);
+	SQAICompanyRegister(vm);
+	SQAIMapRegister(vm);
+	SQAITownRegister(vm);
 
 	/* Reset top */
-	sq_settop(this->vm, top);
+	sq_settop(vm, top);
 }
 
-Squirrel::Squirrel(const char *script_dir)
+Squirrel::Squirrel(const char *script)
 {
-	char filename[1024];
-
-	this->vm = sq_open(1024);
-
-	/* Handle compile-errors ourself, so we can display it nicely */
-	sq_setcompilererrorhandler(this->vm, Squirrel_CompileError);
-	sq_notifyallexceptions(this->vm, SQTrue);
-	/* Set a good print-function */
-	sq_setprintfunc(this->vm, Squirrel_PrintFunc);
-	/* Handle runtime-errors ourself, so we can display it nicely */
-	sq_newclosure(this->vm, &Squirrel_RunError, 0);
-	sq_seterrorhandler(this->vm);
-
-	/* TODO -- This is ugly! */
-	strcpy(filename, "ai/");
-	strcat(filename, script_dir);
-	strcat(filename, PATHSEP);
-	strcat(filename, "main.nut");
-
-	DEBUG(ai, 1, "[squirrel] Starting AI with script in '%s'", filename);
-
-	sq_pushroottable(this->vm);
+	this->core = new SquirrelCore();
 	this->RegisterClasses();
-
-	/* Load and run the script */
-	if (SQ_FAILED(sqstd_dofile(this->vm, filename, SQFalse, SQTrue))) {
-		DEBUG(ai, 0, "[squirrel] Failed to compile '%s'", filename);
-		return;
-	}
+	this->core->LoadScript(script);
 }
 
 Squirrel::~Squirrel()
 {
-	/* Clean up the stuff */
-	sq_pop(this->vm, 1);
-	sq_close(this->vm);
+	delete this->core;
 }
 
 /* virtual */ void Squirrel::GameLoop()
 {
-	/* Store the current top */
-	int top = sq_gettop(this->vm);
-	/* Go to the instance-root */
-	sq_pushobject(this->vm, this->SQ_instance);
-	/* Find the function-name inside the script */
-	sq_pushstring(this->vm, "GameLoop", -1);
-	if (SQ_FAILED(sq_get(this->vm, -2))) {
-		DEBUG(ai, 0, "[squirrel] Could not find 'GameLoop' in the AIController class");
-		sq_settop(this->vm, top);
-		return;
-	}
-	/* Call the method */
-	sq_pushobject(this->vm, this->SQ_instance);
-	sq_call(this->vm, 1, 0, 0);
-	/* Reset the top */
-	sq_settop(this->vm, top);
+	this->core->CallMethod(this->SQ_instance, "GameLoop");
 }
--- a/src/ai/squirrel/squirrel.hpp	Wed Mar 14 23:01:30 2007 +0000
+++ b/src/ai/squirrel/squirrel.hpp	Thu Mar 15 00:01:33 2007 +0000
@@ -13,16 +13,12 @@
 #undef _UNICODE
 #endif
 #include <squirrel.h>
+#include "core.hpp"
 
 class Squirrel: public AIController {
 private:
-	HSQUIRRELVM vm;  ///< The Virtual Machine for squirrel
 	HSQOBJECT SQ_instance; ///< The internal instance of squirrel
-
-	/**
-	 * Add a single method to a class (in creation) in Squirrel.
-	 */
-	void SQAddMethod(const char *name, SQFUNCTION proc, uint nparam, const char *params);
+	SquirrelCore *core;    ///< The SquirrelCore (engine)
 
 	/**
 	 * Registers all our classes, so it can be used from Squirrel.