script_instance.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "../stdafx.h"
00013 #include "../debug.h"
00014 #include "../saveload/saveload.h"
00015 #include "../gui.h"
00016 
00017 #include "../script/squirrel_class.hpp"
00018 
00019 #include "script_fatalerror.hpp"
00020 #include "script_storage.hpp"
00021 #include "script_info.hpp"
00022 #include "script_instance.hpp"
00023 
00024 #include "api/script_controller.hpp"
00025 #include "api/script_error.hpp"
00026 #include "api/script_event.hpp"
00027 #include "api/script_log.hpp"
00028 
00029 #include "../company_base.h"
00030 #include "../company_func.h"
00031 #include "../fileio_func.h"
00032 
00033 ScriptStorage::~ScriptStorage()
00034 {
00035   /* Free our pointers */
00036   if (event_data != NULL) ScriptEventController::FreeEventPointer();
00037   if (log_data != NULL) ScriptLog::FreeLogPointer();
00038 }
00039 
00045 static void PrintFunc(bool error_msg, const SQChar *message)
00046 {
00047   /* Convert to OpenTTD internal capable string */
00048   ScriptController::Print(error_msg, SQ2OTTD(message));
00049 }
00050 
00051 ScriptInstance::ScriptInstance(const char *APIName) :
00052   engine(NULL),
00053   controller(NULL),
00054   storage(NULL),
00055   instance(NULL),
00056   is_started(false),
00057   is_dead(false),
00058   is_save_data_on_stack(false),
00059   suspend(0),
00060   callback(NULL)
00061 {
00062   this->storage = new ScriptStorage();
00063   this->engine  = new Squirrel(APIName);
00064   this->engine->SetPrintFunction(&PrintFunc);
00065 }
00066 
00067 void ScriptInstance::Initialize(const char *main_script, const char *instance_name, CompanyID company)
00068 {
00069   ScriptObject::ActiveInstance active(this);
00070 
00071   this->controller = new ScriptController(company);
00072 
00073   /* Register the API functions and classes */
00074   this->engine->SetGlobalPointer(this->engine);
00075   this->RegisterAPI();
00076 
00077   try {
00078     ScriptObject::SetAllowDoCommand(false);
00079     /* Load and execute the script for this script */
00080     if (strcmp(main_script, "%_dummy") == 0) {
00081       this->LoadDummyScript();
00082     } else if (!this->engine->LoadScript(main_script) || this->engine->IsSuspended()) {
00083       if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to load script. AI is not started.");
00084       this->Died();
00085       return;
00086     }
00087 
00088     /* Create the main-class */
00089     this->instance = MallocT<SQObject>(1);
00090     if (!this->engine->CreateClassInstance(instance_name, this->controller, this->instance)) {
00091       this->Died();
00092       return;
00093     }
00094     ScriptObject::SetAllowDoCommand(true);
00095   } catch (Script_FatalError e) {
00096     this->is_dead = true;
00097     this->engine->ThrowError(e.GetErrorMessage());
00098     this->engine->ResumeError();
00099     this->Died();
00100   }
00101 }
00102 
00103 void ScriptInstance::RegisterAPI()
00104 {
00105   extern void squirrel_register_std(Squirrel *engine);
00106   squirrel_register_std(this->engine);
00107 }
00108 
00109 ScriptInstance::~ScriptInstance()
00110 {
00111   ScriptObject::ActiveInstance active(this);
00112 
00113   if (instance != NULL) this->engine->ReleaseObject(this->instance);
00114   if (engine != NULL) delete this->engine;
00115   delete this->storage;
00116   delete this->controller;
00117   free(this->instance);
00118 }
00119 
00120 void ScriptInstance::Continue()
00121 {
00122   assert(this->suspend < 0);
00123   this->suspend = -this->suspend - 1;
00124 }
00125 
00126 void ScriptInstance::Died()
00127 {
00128   DEBUG(script, 0, "The script died unexpectedly.");
00129   this->is_dead = true;
00130 
00131   if (this->instance != NULL) this->engine->ReleaseObject(this->instance);
00132   delete this->engine;
00133   this->instance = NULL;
00134   this->engine = NULL;
00135 }
00136 
00137 void ScriptInstance::GameLoop()
00138 {
00139   ScriptObject::ActiveInstance active(this);
00140 
00141   if (this->IsDead()) return;
00142   if (this->engine->HasScriptCrashed()) {
00143     /* The script crashed during saving, kill it here. */
00144     this->Died();
00145     return;
00146   }
00147   this->controller->ticks++;
00148 
00149   if (this->suspend   < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
00150   if (this->suspend   < 0)  return;          // Multiplayer suspend, wait for Continue().
00151   if (--this->suspend > 0)  return;          // Singleplayer suspend, decrease to 0.
00152 
00153   _current_company = ScriptObject::GetCompany();
00154 
00155   /* If there is a callback to call, call that first */
00156   if (this->callback != NULL) {
00157     if (this->is_save_data_on_stack) {
00158       sq_poptop(this->engine->GetVM());
00159       this->is_save_data_on_stack = false;
00160     }
00161     try {
00162       this->callback(this);
00163     } catch (Script_Suspend e) {
00164       this->suspend  = e.GetSuspendTime();
00165       this->callback = e.GetSuspendCallback();
00166 
00167       return;
00168     }
00169   }
00170 
00171   this->suspend  = 0;
00172   this->callback = NULL;
00173 
00174   if (!this->is_started) {
00175     try {
00176       ScriptObject::SetAllowDoCommand(false);
00177       /* Run the constructor if it exists. Don't allow any DoCommands in it. */
00178       if (this->engine->MethodExists(*this->instance, "constructor")) {
00179         if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
00180           if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to initialize. Script is not started.");
00181           this->Died();
00182           return;
00183         }
00184       }
00185       if (!this->CallLoad() || this->engine->IsSuspended()) {
00186         if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long in the Load function. Script is not started.");
00187         this->Died();
00188         return;
00189       }
00190       ScriptObject::SetAllowDoCommand(true);
00191       /* Start the script by calling Start() */
00192       if (!this->engine->CallMethod(*this->instance, "Start",  _settings_game.script.script_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
00193     } catch (Script_Suspend e) {
00194       this->suspend  = e.GetSuspendTime();
00195       this->callback = e.GetSuspendCallback();
00196     } catch (Script_FatalError e) {
00197       this->is_dead = true;
00198       this->engine->ThrowError(e.GetErrorMessage());
00199       this->engine->ResumeError();
00200       this->Died();
00201     }
00202 
00203     this->is_started = true;
00204     return;
00205   }
00206   if (this->is_save_data_on_stack) {
00207     sq_poptop(this->engine->GetVM());
00208     this->is_save_data_on_stack = false;
00209   }
00210 
00211   /* Continue the VM */
00212   try {
00213     if (!this->engine->Resume(_settings_game.script.script_max_opcode_till_suspend)) this->Died();
00214   } catch (Script_Suspend e) {
00215     this->suspend  = e.GetSuspendTime();
00216     this->callback = e.GetSuspendCallback();
00217   } catch (Script_FatalError e) {
00218     this->is_dead = true;
00219     this->engine->ThrowError(e.GetErrorMessage());
00220     this->engine->ResumeError();
00221     this->Died();
00222   }
00223 }
00224 
00225 void ScriptInstance::CollectGarbage() const
00226 {
00227   if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
00228 }
00229 
00230 /* static */ void ScriptInstance::DoCommandReturn(ScriptInstance *instance)
00231 {
00232   instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
00233 }
00234 
00235 /* static */ void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance *instance)
00236 {
00237   instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
00238 }
00239 
00240 /* static */ void ScriptInstance::DoCommandReturnSignID(ScriptInstance *instance)
00241 {
00242   instance->engine->InsertResult(ScriptObject::GetNewSignID());
00243 }
00244 
00245 /* static */ void ScriptInstance::DoCommandReturnGroupID(ScriptInstance *instance)
00246 {
00247   instance->engine->InsertResult(ScriptObject::GetNewGroupID());
00248 }
00249 
00250 /* static */ void ScriptInstance::DoCommandReturnGoalID(ScriptInstance *instance)
00251 {
00252   instance->engine->InsertResult(ScriptObject::GetNewGoalID());
00253 }
00254 
00255 ScriptStorage *ScriptInstance::GetStorage()
00256 {
00257   return this->storage;
00258 }
00259 
00260 void *ScriptInstance::GetLogPointer()
00261 {
00262   ScriptObject::ActiveInstance active(this);
00263 
00264   return ScriptObject::GetLogPointer();
00265 }
00266 
00267 /*
00268  * All data is stored in the following format:
00269  * First 1 byte indicating if there is a data blob at all.
00270  * 1 byte indicating the type of data.
00271  * The data itself, this differs per type:
00272  *  - integer: a binary representation of the integer (int32).
00273  *  - string:  First one byte with the string length, then a 0-terminated char
00274  *             array. The string can't be longer than 255 bytes (including
00275  *             terminating '\0').
00276  *  - array:   All data-elements of the array are saved recursive in this
00277  *             format, and ended with an element of the type
00278  *             SQSL_ARRAY_TABLE_END.
00279  *  - table:   All key/value pairs are saved in this format (first key 1, then
00280  *             value 1, then key 2, etc.). All keys and values can have an
00281  *             arbitrary type (as long as it is supported by the save function
00282  *             of course). The table is ended with an element of the type
00283  *             SQSL_ARRAY_TABLE_END.
00284  *  - bool:    A single byte with value 1 representing true and 0 false.
00285  *  - null:    No data.
00286  */
00287 
00289 enum SQSaveLoadType {
00290   SQSL_INT             = 0x00, 
00291   SQSL_STRING          = 0x01, 
00292   SQSL_ARRAY           = 0x02, 
00293   SQSL_TABLE           = 0x03, 
00294   SQSL_BOOL            = 0x04, 
00295   SQSL_NULL            = 0x05, 
00296   SQSL_ARRAY_TABLE_END = 0xFF, 
00297 };
00298 
00299 static byte _script_sl_byte; 
00300 
00302 static const SaveLoad _script_byte[] = {
00303   SLEG_VAR(_script_sl_byte, SLE_UINT8),
00304   SLE_END()
00305 };
00306 
00307 /* static */ bool ScriptInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
00308 {
00309   if (max_depth == 0) {
00310     ScriptLog::Error("Savedata can only be nested to 25 deep. No data saved."); // SQUIRREL_MAX_DEPTH = 25
00311     return false;
00312   }
00313 
00314   switch (sq_gettype(vm, index)) {
00315     case OT_INTEGER: {
00316       if (!test) {
00317         _script_sl_byte = SQSL_INT;
00318         SlObject(NULL, _script_byte);
00319       }
00320       SQInteger res;
00321       sq_getinteger(vm, index, &res);
00322       if (!test) {
00323         int value = (int)res;
00324         SlArray(&value, 1, SLE_INT32);
00325       }
00326       return true;
00327     }
00328 
00329     case OT_STRING: {
00330       if (!test) {
00331         _script_sl_byte = SQSL_STRING;
00332         SlObject(NULL, _script_byte);
00333       }
00334       const SQChar *res;
00335       sq_getstring(vm, index, &res);
00336       /* @bug if a string longer than 512 characters is given to SQ2OTTD, the
00337        *  internal buffer overflows. */
00338       const char *buf = SQ2OTTD(res);
00339       size_t len = strlen(buf) + 1;
00340       if (len >= 255) {
00341         ScriptLog::Error("Maximum string length is 254 chars. No data saved.");
00342         return false;
00343       }
00344       if (!test) {
00345         _script_sl_byte = (byte)len;
00346         SlObject(NULL, _script_byte);
00347         SlArray(const_cast<char *>(buf), len, SLE_CHAR);
00348       }
00349       return true;
00350     }
00351 
00352     case OT_ARRAY: {
00353       if (!test) {
00354         _script_sl_byte = SQSL_ARRAY;
00355         SlObject(NULL, _script_byte);
00356       }
00357       sq_pushnull(vm);
00358       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00359         /* Store the value */
00360         bool res = SaveObject(vm, -1, max_depth - 1, test);
00361         sq_pop(vm, 2);
00362         if (!res) {
00363           sq_pop(vm, 1);
00364           return false;
00365         }
00366       }
00367       sq_pop(vm, 1);
00368       if (!test) {
00369         _script_sl_byte = SQSL_ARRAY_TABLE_END;
00370         SlObject(NULL, _script_byte);
00371       }
00372       return true;
00373     }
00374 
00375     case OT_TABLE: {
00376       if (!test) {
00377         _script_sl_byte = SQSL_TABLE;
00378         SlObject(NULL, _script_byte);
00379       }
00380       sq_pushnull(vm);
00381       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00382         /* Store the key + value */
00383         bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
00384         sq_pop(vm, 2);
00385         if (!res) {
00386           sq_pop(vm, 1);
00387           return false;
00388         }
00389       }
00390       sq_pop(vm, 1);
00391       if (!test) {
00392         _script_sl_byte = SQSL_ARRAY_TABLE_END;
00393         SlObject(NULL, _script_byte);
00394       }
00395       return true;
00396     }
00397 
00398     case OT_BOOL: {
00399       if (!test) {
00400         _script_sl_byte = SQSL_BOOL;
00401         SlObject(NULL, _script_byte);
00402       }
00403       SQBool res;
00404       sq_getbool(vm, index, &res);
00405       if (!test) {
00406         _script_sl_byte = res ? 1 : 0;
00407         SlObject(NULL, _script_byte);
00408       }
00409       return true;
00410     }
00411 
00412     case OT_NULL: {
00413       if (!test) {
00414         _script_sl_byte = SQSL_NULL;
00415         SlObject(NULL, _script_byte);
00416       }
00417       return true;
00418     }
00419 
00420     default:
00421       ScriptLog::Error("You tried to save an unsupported type. No data saved.");
00422       return false;
00423   }
00424 }
00425 
00426 /* static */ void ScriptInstance::SaveEmpty()
00427 {
00428   _script_sl_byte = 0;
00429   SlObject(NULL, _script_byte);
00430 }
00431 
00432 void ScriptInstance::Save()
00433 {
00434   ScriptObject::ActiveInstance active(this);
00435 
00436   /* Don't save data if the script didn't start yet or if it crashed. */
00437   if (this->engine == NULL || this->engine->HasScriptCrashed()) {
00438     SaveEmpty();
00439     return;
00440   }
00441 
00442   HSQUIRRELVM vm = this->engine->GetVM();
00443   if (this->is_save_data_on_stack) {
00444     _script_sl_byte = 1;
00445     SlObject(NULL, _script_byte);
00446     /* Save the data that was just loaded. */
00447     SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
00448   } else if (!this->is_started) {
00449     SaveEmpty();
00450     return;
00451   } else if (this->engine->MethodExists(*this->instance, "Save")) {
00452     HSQOBJECT savedata;
00453     /* We don't want to be interrupted during the save function. */
00454     bool backup_allow = ScriptObject::GetAllowDoCommand();
00455     ScriptObject::SetAllowDoCommand(false);
00456     try {
00457       if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
00458         /* The script crashed in the Save function. We can't kill
00459          * it here, but do so in the next script tick. */
00460         SaveEmpty();
00461         this->engine->CrashOccurred();
00462         return;
00463       }
00464     } catch (Script_FatalError e) {
00465       /* If we don't mark the script as dead here cleaning up the squirrel
00466        * stack could throw Script_FatalError again. */
00467       this->is_dead = true;
00468       this->engine->ThrowError(e.GetErrorMessage());
00469       this->engine->ResumeError();
00470       SaveEmpty();
00471       /* We can't kill the script here, so mark it as crashed (not dead) and
00472        * kill it in the next script tick. */
00473       this->is_dead = false;
00474       this->engine->CrashOccurred();
00475       return;
00476     }
00477     ScriptObject::SetAllowDoCommand(backup_allow);
00478 
00479     if (!sq_istable(savedata)) {
00480       ScriptLog::Error(this->engine->IsSuspended() ? "This script took too long to Save." : "Save function should return a table.");
00481       SaveEmpty();
00482       this->engine->CrashOccurred();
00483       return;
00484     }
00485     sq_pushobject(vm, savedata);
00486     if (SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, true)) {
00487       _script_sl_byte = 1;
00488       SlObject(NULL, _script_byte);
00489       SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
00490       this->is_save_data_on_stack = true;
00491     } else {
00492       SaveEmpty();
00493       this->engine->CrashOccurred();
00494     }
00495   } else {
00496     ScriptLog::Warning("Save function is not implemented");
00497     _script_sl_byte = 0;
00498     SlObject(NULL, _script_byte);
00499   }
00500 }
00501 
00502 void ScriptInstance::Suspend()
00503 {
00504   HSQUIRRELVM vm = this->engine->GetVM();
00505   Squirrel::DecreaseOps(vm, _settings_game.script.script_max_opcode_till_suspend);
00506 }
00507 
00508 /* static */ bool ScriptInstance::LoadObjects(HSQUIRRELVM vm)
00509 {
00510   SlObject(NULL, _script_byte);
00511   switch (_script_sl_byte) {
00512     case SQSL_INT: {
00513       int value;
00514       SlArray(&value, 1, SLE_INT32);
00515       if (vm != NULL) sq_pushinteger(vm, (SQInteger)value);
00516       return true;
00517     }
00518 
00519     case SQSL_STRING: {
00520       SlObject(NULL, _script_byte);
00521       static char buf[256];
00522       SlArray(buf, _script_sl_byte, SLE_CHAR);
00523       if (vm != NULL) sq_pushstring(vm, OTTD2SQ(buf), -1);
00524       return true;
00525     }
00526 
00527     case SQSL_ARRAY: {
00528       if (vm != NULL) sq_newarray(vm, 0);
00529       while (LoadObjects(vm)) {
00530         if (vm != NULL) sq_arrayappend(vm, -2);
00531         /* The value is popped from the stack by squirrel. */
00532       }
00533       return true;
00534     }
00535 
00536     case SQSL_TABLE: {
00537       if (vm != NULL) sq_newtable(vm);
00538       while (LoadObjects(vm)) {
00539         LoadObjects(vm);
00540         if (vm != NULL) sq_rawset(vm, -3);
00541         /* The key (-2) and value (-1) are popped from the stack by squirrel. */
00542       }
00543       return true;
00544     }
00545 
00546     case SQSL_BOOL: {
00547       SlObject(NULL, _script_byte);
00548       if (vm != NULL) sq_pushinteger(vm, (SQBool)(_script_sl_byte != 0));
00549       return true;
00550     }
00551 
00552     case SQSL_NULL: {
00553       if (vm != NULL) sq_pushnull(vm);
00554       return true;
00555     }
00556 
00557     case SQSL_ARRAY_TABLE_END: {
00558       return false;
00559     }
00560 
00561     default: NOT_REACHED();
00562   }
00563 }
00564 
00565 /* static */ void ScriptInstance::LoadEmpty()
00566 {
00567   SlObject(NULL, _script_byte);
00568   /* Check if there was anything saved at all. */
00569   if (_script_sl_byte == 0) return;
00570 
00571   LoadObjects(NULL);
00572 }
00573 
00574 void ScriptInstance::Load(int version)
00575 {
00576   ScriptObject::ActiveInstance active(this);
00577 
00578   if (this->engine == NULL || version == -1) {
00579     LoadEmpty();
00580     return;
00581   }
00582   HSQUIRRELVM vm = this->engine->GetVM();
00583 
00584   SlObject(NULL, _script_byte);
00585   /* Check if there was anything saved at all. */
00586   if (_script_sl_byte == 0) return;
00587 
00588   sq_pushinteger(vm, version);
00589   LoadObjects(vm);
00590   this->is_save_data_on_stack = true;
00591 }
00592 
00593 bool ScriptInstance::CallLoad()
00594 {
00595   HSQUIRRELVM vm = this->engine->GetVM();
00596   /* Is there save data that we should load? */
00597   if (!this->is_save_data_on_stack) return true;
00598   /* Whatever happens, after CallLoad the savegame data is removed from the stack. */
00599   this->is_save_data_on_stack = false;
00600 
00601   if (!this->engine->MethodExists(*this->instance, "Load")) {
00602     ScriptLog::Warning("Loading failed: there was data for the script to load, but the script does not have a Load() function.");
00603 
00604     /* Pop the savegame data and version. */
00605     sq_pop(vm, 2);
00606     return true;
00607   }
00608 
00609   /* Go to the instance-root */
00610   sq_pushobject(vm, *this->instance);
00611   /* Find the function-name inside the script */
00612   sq_pushstring(vm, OTTD2SQ("Load"), -1);
00613   /* Change the "Load" string in a function pointer */
00614   sq_get(vm, -2);
00615   /* Push the main instance as "this" object */
00616   sq_pushobject(vm, *this->instance);
00617   /* Push the version data and savegame data as arguments */
00618   sq_push(vm, -5);
00619   sq_push(vm, -5);
00620 
00621   /* Call the script load function. sq_call removes the arguments (but not the
00622    * function pointer) from the stack. */
00623   if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
00624 
00625   /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
00626   sq_pop(vm, 4);
00627   return true;
00628 }
00629 
00630 SQInteger ScriptInstance::GetOpsTillSuspend()
00631 {
00632   return this->engine->GetOpsTillSuspend();
00633 }
00634 
00635 void ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
00636 {
00637   ScriptObject::ActiveInstance active(this);
00638 
00639   ScriptObject::SetLastCommandRes(result.Succeeded());
00640 
00641   if (result.Failed()) {
00642     ScriptObject::SetLastError(ScriptError::StringToError(result.GetErrorMessage()));
00643   } else {
00644     ScriptObject::IncreaseDoCommandCosts(result.GetCost());
00645     ScriptObject::SetLastCost(result.GetCost());
00646   }
00647 }
00648 
00649 void ScriptInstance::InsertEvent(class ScriptEvent *event)
00650 {
00651   ScriptObject::ActiveInstance active(this);
00652 
00653   ScriptEventController::InsertEvent(event);
00654 }