src/ai/squirrel.cpp
branchnoai
changeset 9427 ef0c109c5661
parent 9426 b90c0d1a36b7
child 9429 25b7d020a3a9
equal deleted inserted replaced
9426:b90c0d1a36b7 9427:ef0c109c5661
       
     1 /* $Id$ */
       
     2 
       
     3 /** @file squirrel.cpp allows loading squirrel scripts to control an AI */
       
     4 
       
     5 #include "../../stdafx.h"
       
     6 #include "../../debug.h"
       
     7 #include "../../openttd.h"
       
     8 #include "../../string.h"
       
     9 #include "../../fios.h"
       
    10 #include <sys/types.h>
       
    11 #include <sys/stat.h>
       
    12 
       
    13 #ifdef _UNICODE
       
    14 /* Disable unicode for squirrel to allow compilation with MINGW
       
    15  * and simplify coding for WIN32 (squirrel headers miss a lot of "string" functions)
       
    16  */
       
    17 #undef _UNICODE
       
    18 #endif
       
    19 #include <squirrel.h>
       
    20 #include "../../squirrel.hpp"
       
    21 #include "../../squirrel_helper.hpp"
       
    22 #include "../../squirrel_class.hpp"
       
    23 #include "../core/ai_factory.hpp"
       
    24 #include "../core/ai_controller.hpp"
       
    25 #include "squirrel.hpp"
       
    26 
       
    27 /* Convert all AI related classes to Squirrel data */
       
    28 #define DEFINE_SQUIRREL_CLASS
       
    29 #include "../core/ai_base.hpp"
       
    30 #include "../core/ai_cargo.hpp"
       
    31 #include "../core/ai_controller.hpp"
       
    32 #include "../core/ai_company.hpp"
       
    33 #include "../core/ai_industry.hpp"
       
    34 #include "../core/ai_map.hpp"
       
    35 #include "../core/ai_town.hpp"
       
    36 
       
    37 static FSquirrel iFSquirrel; ///< Tell the AI-core that we have an AI with which we like to play.
       
    38 
       
    39 /**
       
    40  * Our tiny wrapper between C++ and Squirrel.
       
    41  */
       
    42 class AIFactorySquirrel: public AIFactoryBase {
       
    43 public:
       
    44 	Squirrel *engine;
       
    45 	HSQOBJECT SQ_instance;
       
    46 	char *script_name;
       
    47 
       
    48 	~AIFactorySquirrel();
       
    49 
       
    50 	/* virtual */ const char   *GetAuthor()      { return this->engine->CallStringMethod (this->SQ_instance, "GetAuthor"); }
       
    51 	/* virtual */ const char   *GetName()        { return this->engine->CallStringMethod (this->SQ_instance, "GetName"); }
       
    52 	/* virtual */ const char   *GetDescription() { return this->engine->CallStringMethod (this->SQ_instance, "GetDescription"); }
       
    53 	/* virtual */ int           GetVersion()     { return this->engine->CallIntegerMethod(this->SQ_instance, "GetVersion"); }
       
    54 	/* virtual */ const char   *GetDate()        { return this->engine->CallStringMethod (this->SQ_instance, "GetDate"); }
       
    55 	/* virtual */ AIController *CreateInstance();
       
    56 
       
    57 	/**
       
    58 	 * Register Squirrel script to the Factory.
       
    59 	 */
       
    60 	void RegisterSquirrel()        { this->RegisterFactory(this->GetName()); }
       
    61 
       
    62 	/**
       
    63 	 * Check if a method exists in the script.
       
    64 	 */
       
    65 	void CheckMethods(AIFactorySquirrel *fbase, SQInteger *res, const char *name);
       
    66 };
       
    67 
       
    68 AIFactorySquirrel::~AIFactorySquirrel()
       
    69 {
       
    70 	free(this->script_name);
       
    71 }
       
    72 
       
    73 void AIFactorySquirrel::CheckMethods(AIFactorySquirrel *fbase, SQInteger *res, const char *name)
       
    74 {
       
    75 	if (!fbase->engine->MethodExists(fbase->SQ_instance, name)) {
       
    76 		char error[1024];
       
    77 		snprintf(error, sizeof(error), "Missing method '%s' for FactoryClass", name);
       
    78 		fbase->engine->ThrowError(error);
       
    79 		*res = SQ_ERROR;
       
    80 	}
       
    81 }
       
    82 
       
    83 SQInteger FSquirrel::FactoryConstructor(HSQUIRRELVM vm)
       
    84 {
       
    85 	AIFactorySquirrel *fbase = new AIFactorySquirrel();
       
    86 	SQInteger res = 0;
       
    87 
       
    88 	Squirrel::GetInstance(vm, &fbase->SQ_instance);
       
    89 	fbase->engine = ((FSquirrel *)Squirrel::GetGlobalPointer(vm))->engine;
       
    90 
       
    91 	/* Check if all needed fields are there */
       
    92 	fbase->CheckMethods(fbase, &res, "GetAuthor");
       
    93 	fbase->CheckMethods(fbase, &res, "GetName");
       
    94 	fbase->CheckMethods(fbase, &res, "GetDescription");
       
    95 	fbase->CheckMethods(fbase, &res, "GetVersion");
       
    96 	fbase->CheckMethods(fbase, &res, "GetDate");
       
    97 	fbase->CheckMethods(fbase, &res, "CreateInstance");
       
    98 
       
    99 	/* Abort if one method was missing */
       
   100 	if (res != 0) return res;
       
   101 
       
   102 	fbase->RegisterSquirrel();
       
   103 	fbase->script_name = strdup(((FSquirrel *)Squirrel::GetGlobalPointer(vm))->current_script);
       
   104 
       
   105 	return 0;
       
   106 }
       
   107 
       
   108 extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
       
   109 extern bool FiosIsHiddenFile(const struct dirent *ent);
       
   110 
       
   111 
       
   112 void FSquirrel::ScanDir(const char *dirname)
       
   113 {
       
   114 	struct stat sb;
       
   115 	struct dirent *dirent;
       
   116 	DIR *dir;
       
   117 	char d_name[256];
       
   118 	char script_name[256];
       
   119 	dir = ttd_opendir(dirname);
       
   120 	/* Dir not found, so do nothing */
       
   121 	if (dir == NULL) return;
       
   122 
       
   123 	/* Walk all dirs trying to find a dir in which 'main.nut' exists */
       
   124 	while ((dirent = readdir(dir)) != NULL) {
       
   125 		ttd_strlcpy(d_name, FS2OTTD(dirent->d_name), sizeof(d_name));
       
   126 
       
   127 		/* Found file must be directory, but not '.' or '..' */
       
   128 		if (FiosIsValidFile("ai", dirent, &sb) && (sb.st_mode & S_IFDIR) &&
       
   129 				(!FiosIsHiddenFile(dirent) || strncasecmp(d_name, PERSONAL_DIR, strlen(d_name)) == 0) &&
       
   130 				strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) {
       
   131 			/* Create the full-length script-name */
       
   132 			strcpy(script_name, dirname);
       
   133 			strcat(script_name, PATHSEP);
       
   134 			strcat(script_name, d_name);
       
   135 			strcat(script_name, PATHSEP);
       
   136 			strcat(script_name, "main.nut");
       
   137 			/* If it exists, load it up */
       
   138 			if (FileExists(script_name)) {
       
   139 				DEBUG(ai, 6, "[squirrel] Loading script '%s' for AI handling", script_name);
       
   140 				this->current_script = script_name;
       
   141 				this->engine->LoadScript(this->current_script);
       
   142 			}
       
   143 		}
       
   144 	}
       
   145 	closedir(dir);
       
   146 }
       
   147 
       
   148 void FSquirrel::Initializer()
       
   149 {
       
   150 	this->engine = new Squirrel();
       
   151 
       
   152 	/* Create the AIFactory class, and bind the constructor */
       
   153 	this->engine->AddClassBegin("AIFactory");
       
   154 	this->engine->AddMethod("constructor", &FSquirrel::FactoryConstructor, 1, "x");
       
   155 	this->engine->AddClassEnd();
       
   156 
       
   157 	/* Set a dummy AIController, so script can load */
       
   158 	this->engine->AddClassBegin("AIController");
       
   159 	this->engine->AddClassEnd();
       
   160 	/* Mark this class as global pointer */
       
   161 	this->engine->SetGlobalPointer(this);
       
   162 
       
   163 	/* Scan the AI dir for scripts */
       
   164 	this->ScanDir("ai");
       
   165 }
       
   166 
       
   167 FSquirrel::~FSquirrel()
       
   168 {
       
   169 	delete this->engine;
       
   170 }
       
   171 
       
   172 AIController *AIFactorySquirrel::CreateInstance()
       
   173 {
       
   174 	const char *class_name = this->engine->CallStringMethod(this->SQ_instance, "CreateInstance");
       
   175 	AIControllerSquirrel *controller = new AIControllerSquirrel(this->script_name, class_name);
       
   176 
       
   177 	return controller;
       
   178 }
       
   179 
       
   180 void AIControllerSquirrel::RegisterClasses()
       
   181 {
       
   182 	/* Ignore AIFactory if we are really starting an AI */
       
   183 	this->engine->AddClassBegin("AIFactory");
       
   184 	this->engine->AddClassEnd();
       
   185 
       
   186 	/* Register all classes */
       
   187 	SQAIBaseRegister(this->engine);
       
   188 	SQAICargoRegister(this->engine);
       
   189 	SQAICompanyRegister(this->engine);
       
   190 	SQAIControllerRegister(this->engine);
       
   191 	SQAIIndustryRegister(this->engine);
       
   192 	SQAIMapRegister(this->engine);
       
   193 	SQAITownRegister(this->engine);
       
   194 
       
   195 	this->engine->SetGlobalPointer(this->engine);
       
   196 }
       
   197 
       
   198 AIControllerSquirrel::AIControllerSquirrel(const char *script, const char *class_name)
       
   199 {
       
   200 	this->engine = new Squirrel();
       
   201 	this->RegisterClasses();
       
   202 	this->engine->LoadScript(script);
       
   203 	/* Create the main-class */
       
   204 	this->engine->CreateClassInstance(class_name, this, &this->SQ_instance);
       
   205 	this->engine->CallMethod(this->SQ_instance, "constructor");
       
   206 }
       
   207 
       
   208 AIControllerSquirrel::~AIControllerSquirrel()
       
   209 {
       
   210 	delete this->engine;
       
   211 }
       
   212 
       
   213 /* virtual */ void AIControllerSquirrel::GameLoop()
       
   214 {
       
   215 	this->engine->CallMethod(this->SQ_instance, "GameLoop");
       
   216 }