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