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)
00068 {
00069   ScriptObject::ActiveInstance active(this);
00070 
00071   this->controller = new ScriptController();
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   /* If there is a callback to call, call that first */
00154   if (this->callback != NULL) {
00155     if (this->is_save_data_on_stack) {
00156       sq_poptop(this->engine->GetVM());
00157       this->is_save_data_on_stack = false;
00158     }
00159     try {
00160       this->callback(this);
00161     } catch (Script_Suspend e) {
00162       this->suspend  = e.GetSuspendTime();
00163       this->callback = e.GetSuspendCallback();
00164 
00165       return;
00166     }
00167   }
00168 
00169   this->suspend  = 0;
00170   this->callback = NULL;
00171 
00172   if (!this->is_started) {
00173     try {
00174       ScriptObject::SetAllowDoCommand(false);
00175       /* Run the constructor if it exists. Don't allow any DoCommands in it. */
00176       if (this->engine->MethodExists(*this->instance, "constructor")) {
00177         if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
00178           if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to initialize. Script is not started.");
00179           this->Died();
00180           return;
00181         }
00182       }
00183       if (!this->CallLoad() || this->engine->IsSuspended()) {
00184         if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long in the Load function. Script is not started.");
00185         this->Died();
00186         return;
00187       }
00188       ScriptObject::SetAllowDoCommand(true);
00189       /* Start the script by calling Start() */
00190       if (!this->engine->CallMethod(*this->instance, "Start",  _settings_game.script.script_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
00191     } catch (Script_Suspend e) {
00192       this->suspend  = e.GetSuspendTime();
00193       this->callback = e.GetSuspendCallback();
00194     } catch (Script_FatalError e) {
00195       this->is_dead = true;
00196       this->engine->ThrowError(e.GetErrorMessage());
00197       this->engine->ResumeError();
00198       this->Died();
00199     }
00200 
00201     this->is_started = true;
00202     return;
00203   }
00204   if (this->is_save_data_on_stack) {
00205     sq_poptop(this->engine->GetVM());
00206     this->is_save_data_on_stack = false;
00207   }
00208 
00209   /* Continue the VM */
00210   try {
00211     if (!this->engine->Resume(_settings_game.script.script_max_opcode_till_suspend)) this->Died();
00212   } catch (Script_Suspend e) {
00213     this->suspend  = e.GetSuspendTime();
00214     this->callback = e.GetSuspendCallback();
00215   } catch (Script_FatalError e) {
00216     this->is_dead = true;
00217     this->engine->ThrowError(e.GetErrorMessage());
00218     this->engine->ResumeError();
00219     this->Died();
00220   }
00221 }
00222 
00223 void ScriptInstance::CollectGarbage() const
00224 {
00225   if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
00226 }
00227 
00228 /* static */ void ScriptInstance::DoCommandReturn(ScriptInstance *instance)
00229 {
00230   instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
00231 }
00232 
00233 /* static */ void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance *instance)
00234 {
00235   instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
00236 }
00237 
00238 /* static */ void ScriptInstance::DoCommandReturnSignID(ScriptInstance *instance)
00239 {
00240   instance->engine->InsertResult(ScriptObject::GetNewSignID());
00241 }
00242 
00243 /* static */ void ScriptInstance::DoCommandReturnGroupID(ScriptInstance *instance)
00244 {
00245   instance->engine->InsertResult(ScriptObject::GetNewGroupID());
00246 }
00247 
00248 ScriptStorage *ScriptInstance::GetStorage()
00249 {
00250   return this->storage;
00251 }
00252 
00253 void *ScriptInstance::GetLogPointer()
00254 {
00255   ScriptObject::ActiveInstance active(this);
00256 
00257   return ScriptObject::GetLogPointer();
00258 }
00259 
00260 /*
00261  * All data is stored in the following format:
00262  * First 1 byte indicating if there is a data blob at all.
00263  * 1 byte indicating the type of data.
00264  * The data itself, this differs per type:
00265  *  - integer: a binary representation of the integer (int32).
00266  *  - string:  First one byte with the string length, then a 0-terminated char
00267  *             array. The string can't be longer than 255 bytes (including
00268  *             terminating '\0').
00269  *  - array:   All data-elements of the array are saved recursive in this
00270  *             format, and ended with an element of the type
00271  *             SQSL_ARRAY_TABLE_END.
00272  *  - table:   All key/value pairs are saved in this format (first key 1, then
00273  *             value 1, then key 2, etc.). All keys and values can have an
00274  *             arbitrary type (as long as it is supported by the save function
00275  *             of course). The table is ended with an element of the type
00276  *             SQSL_ARRAY_TABLE_END.
00277  *  - bool:    A single byte with value 1 representing true and 0 false.
00278  *  - null:    No data.
00279  */
00280 
00282 enum SQSaveLoadType {
00283   SQSL_INT             = 0x00, 
00284   SQSL_STRING          = 0x01, 
00285   SQSL_ARRAY           = 0x02, 
00286   SQSL_TABLE           = 0x03, 
00287   SQSL_BOOL            = 0x04, 
00288   SQSL_NULL            = 0x05, 
00289   SQSL_ARRAY_TABLE_END = 0xFF, 
00290 };
00291 
00292 static byte _script_sl_byte; 
00293 
00295 static const SaveLoad _script_byte[] = {
00296   SLEG_VAR(_script_sl_byte, SLE_UINT8),
00297   SLE_END()
00298 };
00299 
00300 static const uint SCRIPTSAVE_MAX_DEPTH = 25; 
00301 
00302 /* static */ bool ScriptInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
00303 {
00304   if (max_depth == 0) {
00305     ScriptLog::Error("Savedata can only be nested to 25 deep. No data saved.");
00306     return false;
00307   }
00308 
00309   switch (sq_gettype(vm, index)) {
00310     case OT_INTEGER: {
00311       if (!test) {
00312         _script_sl_byte = SQSL_INT;
00313         SlObject(NULL, _script_byte);
00314       }
00315       SQInteger res;
00316       sq_getinteger(vm, index, &res);
00317       if (!test) {
00318         int value = (int)res;
00319         SlArray(&value, 1, SLE_INT32);
00320       }
00321       return true;
00322     }
00323 
00324     case OT_STRING: {
00325       if (!test) {
00326         _script_sl_byte = SQSL_STRING;
00327         SlObject(NULL, _script_byte);
00328       }
00329       const SQChar *res;
00330       sq_getstring(vm, index, &res);
00331       /* @bug if a string longer than 512 characters is given to SQ2OTTD, the
00332        *  internal buffer overflows. */
00333       const char *buf = SQ2OTTD(res);
00334       size_t len = strlen(buf) + 1;
00335       if (len >= 255) {
00336         ScriptLog::Error("Maximum string length is 254 chars. No data saved.");
00337         return false;
00338       }
00339       if (!test) {
00340         _script_sl_byte = (byte)len;
00341         SlObject(NULL, _script_byte);
00342         SlArray(const_cast<char *>(buf), len, SLE_CHAR);
00343       }
00344       return true;
00345     }
00346 
00347     case OT_ARRAY: {
00348       if (!test) {
00349         _script_sl_byte = SQSL_ARRAY;
00350         SlObject(NULL, _script_byte);
00351       }
00352       sq_pushnull(vm);
00353       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00354         /* Store the value */
00355         bool res = SaveObject(vm, -1, max_depth - 1, test);
00356         sq_pop(vm, 2);
00357         if (!res) {
00358           sq_pop(vm, 1);
00359           return false;
00360         }
00361       }
00362       sq_pop(vm, 1);
00363       if (!test) {
00364         _script_sl_byte = SQSL_ARRAY_TABLE_END;
00365         SlObject(NULL, _script_byte);
00366       }
00367       return true;
00368     }
00369 
00370     case OT_TABLE: {
00371       if (!test) {
00372         _script_sl_byte = SQSL_TABLE;
00373         SlObject(NULL, _script_byte);
00374       }
00375       sq_pushnull(vm);
00376       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00377         /* Store the key + value */
00378         bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
00379         sq_pop(vm, 2);
00380         if (!res) {
00381           sq_pop(vm, 1);
00382           return false;
00383         }
00384       }
00385       sq_pop(vm, 1);
00386       if (!test) {
00387         _script_sl_byte = SQSL_ARRAY_TABLE_END;
00388         SlObject(NULL, _script_byte);
00389       }
00390       return true;
00391     }
00392 
00393     case OT_BOOL: {
00394       if (!test) {
00395         _script_sl_byte = SQSL_BOOL;
00396         SlObject(NULL, _script_byte);
00397       }
00398       SQBool res;
00399       sq_getbool(vm, index, &res);
00400       if (!test) {
00401         _script_sl_byte = res ? 1 : 0;
00402         SlObject(NULL, _script_byte);
00403       }
00404       return true;
00405     }
00406 
00407     case OT_NULL: {
00408       if (!test) {
00409         _script_sl_byte = SQSL_NULL;
00410         SlObject(NULL, _script_byte);
00411       }
00412       return true;
00413     }
00414 
00415     default:
00416       ScriptLog::Error("You tried to save an unsupported type. No data saved.");
00417       return false;
00418   }
00419 }
00420 
00421 /* static */ void ScriptInstance::SaveEmpty()
00422 {
00423   _script_sl_byte = 0;
00424   SlObject(NULL, _script_byte);
00425 }
00426 
00427 void ScriptInstance::Save()
00428 {
00429   ScriptObject::ActiveInstance active(this);
00430 
00431   /* Don't save data if the script didn't start yet or if it crashed. */
00432   if (this->engine == NULL || this->engine->HasScriptCrashed()) {
00433     SaveEmpty();
00434     return;
00435   }
00436 
00437   HSQUIRRELVM vm = this->engine->GetVM();
00438   if (this->is_save_data_on_stack) {
00439     _script_sl_byte = 1;
00440     SlObject(NULL, _script_byte);
00441     /* Save the data that was just loaded. */
00442     SaveObject(vm, -1, SCRIPTSAVE_MAX_DEPTH, false);
00443   } else if (!this->is_started) {
00444     SaveEmpty();
00445     return;
00446   } else if (this->engine->MethodExists(*this->instance, "Save")) {
00447     HSQOBJECT savedata;
00448     /* We don't want to be interrupted during the save function. */
00449     bool backup_allow = ScriptObject::GetAllowDoCommand();
00450     ScriptObject::SetAllowDoCommand(false);
00451     try {
00452       if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
00453         /* The script crashed in the Save function. We can't kill
00454          * it here, but do so in the next script tick. */
00455         SaveEmpty();
00456         this->engine->CrashOccurred();
00457         return;
00458       }
00459     } catch (Script_FatalError e) {
00460       /* If we don't mark the script as dead here cleaning up the squirrel
00461        * stack could throw Script_FatalError again. */
00462       this->is_dead = true;
00463       this->engine->ThrowError(e.GetErrorMessage());
00464       this->engine->ResumeError();
00465       SaveEmpty();
00466       /* We can't kill the script here, so mark it as crashed (not dead) and
00467        * kill it in the next script tick. */
00468       this->is_dead = false;
00469       this->engine->CrashOccurred();
00470       return;
00471     }
00472     ScriptObject::SetAllowDoCommand(backup_allow);
00473 
00474     if (!sq_istable(savedata)) {
00475       ScriptLog::Error(this->engine->IsSuspended() ? "This script took too long to Save." : "Save function should return a table.");
00476       SaveEmpty();
00477       this->engine->CrashOccurred();
00478       return;
00479     }
00480     sq_pushobject(vm, savedata);
00481     if (SaveObject(vm, -1, SCRIPTSAVE_MAX_DEPTH, true)) {
00482       _script_sl_byte = 1;
00483       SlObject(NULL, _script_byte);
00484       SaveObject(vm, -1, SCRIPTSAVE_MAX_DEPTH, false);
00485       this->is_save_data_on_stack = true;
00486     } else {
00487       SaveEmpty();
00488       this->engine->CrashOccurred();
00489     }
00490   } else {
00491     ScriptLog::Warning("Save function is not implemented");
00492     _script_sl_byte = 0;
00493     SlObject(NULL, _script_byte);
00494   }
00495 }
00496 
00497 void ScriptInstance::Suspend()
00498 {
00499   HSQUIRRELVM vm = this->engine->GetVM();
00500   Squirrel::DecreaseOps(vm, _settings_game.script.script_max_opcode_till_suspend);
00501 }
00502 
00503 /* static */ bool ScriptInstance::LoadObjects(HSQUIRRELVM vm)
00504 {
00505   SlObject(NULL, _script_byte);
00506   switch (_script_sl_byte) {
00507     case SQSL_INT: {
00508       int value;
00509       SlArray(&value, 1, SLE_INT32);
00510       if (vm != NULL) sq_pushinteger(vm, (SQInteger)value);
00511       return true;
00512     }
00513 
00514     case SQSL_STRING: {
00515       SlObject(NULL, _script_byte);
00516       static char buf[256];
00517       SlArray(buf, _script_sl_byte, SLE_CHAR);
00518       if (vm != NULL) sq_pushstring(vm, OTTD2SQ(buf), -1);
00519       return true;
00520     }
00521 
00522     case SQSL_ARRAY: {
00523       if (vm != NULL) sq_newarray(vm, 0);
00524       while (LoadObjects(vm)) {
00525         if (vm != NULL) sq_arrayappend(vm, -2);
00526         /* The value is popped from the stack by squirrel. */
00527       }
00528       return true;
00529     }
00530 
00531     case SQSL_TABLE: {
00532       if (vm != NULL) sq_newtable(vm);
00533       while (LoadObjects(vm)) {
00534         LoadObjects(vm);
00535         if (vm != NULL) sq_rawset(vm, -3);
00536         /* The key (-2) and value (-1) are popped from the stack by squirrel. */
00537       }
00538       return true;
00539     }
00540 
00541     case SQSL_BOOL: {
00542       SlObject(NULL, _script_byte);
00543       if (vm != NULL) sq_pushinteger(vm, (SQBool)(_script_sl_byte != 0));
00544       return true;
00545     }
00546 
00547     case SQSL_NULL: {
00548       if (vm != NULL) sq_pushnull(vm);
00549       return true;
00550     }
00551 
00552     case SQSL_ARRAY_TABLE_END: {
00553       return false;
00554     }
00555 
00556     default: NOT_REACHED();
00557   }
00558 }
00559 
00560 /* static */ void ScriptInstance::LoadEmpty()
00561 {
00562   SlObject(NULL, _script_byte);
00563   /* Check if there was anything saved at all. */
00564   if (_script_sl_byte == 0) return;
00565 
00566   LoadObjects(NULL);
00567 }
00568 
00569 void ScriptInstance::Load(int version)
00570 {
00571   ScriptObject::ActiveInstance active(this);
00572 
00573   if (this->engine == NULL || version == -1) {
00574     LoadEmpty();
00575     return;
00576   }
00577   HSQUIRRELVM vm = this->engine->GetVM();
00578 
00579   SlObject(NULL, _script_byte);
00580   /* Check if there was anything saved at all. */
00581   if (_script_sl_byte == 0) return;
00582 
00583   sq_pushinteger(vm, version);
00584   LoadObjects(vm);
00585   this->is_save_data_on_stack = true;
00586 }
00587 
00588 bool ScriptInstance::CallLoad()
00589 {
00590   HSQUIRRELVM vm = this->engine->GetVM();
00591   /* Is there save data that we should load? */
00592   if (!this->is_save_data_on_stack) return true;
00593   /* Whatever happens, after CallLoad the savegame data is removed from the stack. */
00594   this->is_save_data_on_stack = false;
00595 
00596   if (!this->engine->MethodExists(*this->instance, "Load")) {
00597     ScriptLog::Warning("Loading failed: there was data for the script to load, but the script does not have a Load() function.");
00598 
00599     /* Pop the savegame data and version. */
00600     sq_pop(vm, 2);
00601     return true;
00602   }
00603 
00604   /* Go to the instance-root */
00605   sq_pushobject(vm, *this->instance);
00606   /* Find the function-name inside the script */
00607   sq_pushstring(vm, OTTD2SQ("Load"), -1);
00608   /* Change the "Load" string in a function pointer */
00609   sq_get(vm, -2);
00610   /* Push the main instance as "this" object */
00611   sq_pushobject(vm, *this->instance);
00612   /* Push the version data and savegame data as arguments */
00613   sq_push(vm, -5);
00614   sq_push(vm, -5);
00615 
00616   /* Call the script load function. sq_call removes the arguments (but not the
00617    * function pointer) from the stack. */
00618   if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
00619 
00620   /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
00621   sq_pop(vm, 4);
00622   return true;
00623 }
00624 
00625 SQInteger ScriptInstance::GetOpsTillSuspend()
00626 {
00627   return this->engine->GetOpsTillSuspend();
00628 }
00629 
00630 void ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
00631 {
00632   ScriptObject::ActiveInstance active(this);
00633 
00634   ScriptObject::SetLastCommandRes(result.Succeeded());
00635 
00636   if (result.Failed()) {
00637     ScriptObject::SetLastError(ScriptError::StringToError(result.GetErrorMessage()));
00638   } else {
00639     ScriptObject::IncreaseDoCommandCosts(result.GetCost());
00640     ScriptObject::SetLastCost(result.GetCost());
00641   }
00642 }
00643 
00644 void ScriptInstance::InsertEvent(class ScriptEvent *event)
00645 {
00646   ScriptObject::ActiveInstance active(this);
00647 
00648   ScriptEventController::InsertEvent(event);
00649 }