script_instance.cpp

Go to the documentation of this file.
00001 /* $Id: script_instance.cpp 24538 2012-09-21 20:27:09Z yexo $ */
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 #include "../fileio_func.h"
00031 
00032 ScriptStorage::~ScriptStorage()
00033 {
00034   /* Free our pointers */
00035   if (event_data != NULL) ScriptEventController::FreeEventPointer();
00036   if (log_data != NULL) ScriptLog::FreeLogPointer();
00037 }
00038 
00044 static void PrintFunc(bool error_msg, const SQChar *message)
00045 {
00046   /* Convert to OpenTTD internal capable string */
00047   ScriptController::Print(error_msg, SQ2OTTD(message));
00048 }
00049 
00050 ScriptInstance::ScriptInstance(const char *APIName) :
00051   engine(NULL),
00052   controller(NULL),
00053   storage(NULL),
00054   instance(NULL),
00055   is_started(false),
00056   is_dead(false),
00057   is_save_data_on_stack(false),
00058   suspend(0),
00059   is_paused(false),
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 bool ScriptInstance::LoadCompatibilityScripts(const char *api_version, Subdirectory dir)
00110 {
00111   char script_name[32];
00112   seprintf(script_name, lastof(script_name), "compat_%s.nut", api_version);
00113   char buf[MAX_PATH];
00114   Searchpath sp;
00115   FOR_ALL_SEARCHPATHS(sp) {
00116     FioAppendDirectory(buf, MAX_PATH, sp, dir);
00117     ttd_strlcat(buf, script_name, MAX_PATH);
00118     if (!FileExists(buf)) continue;
00119 
00120     if (this->engine->LoadScript(buf)) return true;
00121 
00122     ScriptLog::Error("Failed to load API compatibility script");
00123     DEBUG(script, 0, "Error compiling / running API compatibility script: %s", buf);
00124     return false;
00125   }
00126 
00127   ScriptLog::Warning("API compatibility script not found");
00128   return true;
00129 }
00130 
00131 ScriptInstance::~ScriptInstance()
00132 {
00133   ScriptObject::ActiveInstance active(this);
00134 
00135   if (instance != NULL) this->engine->ReleaseObject(this->instance);
00136   if (engine != NULL) delete this->engine;
00137   delete this->storage;
00138   delete this->controller;
00139   free(this->instance);
00140 }
00141 
00142 void ScriptInstance::Continue()
00143 {
00144   assert(this->suspend < 0);
00145   this->suspend = -this->suspend - 1;
00146 }
00147 
00148 void ScriptInstance::Died()
00149 {
00150   DEBUG(script, 0, "The script died unexpectedly.");
00151   this->is_dead = true;
00152 
00153   if (this->instance != NULL) this->engine->ReleaseObject(this->instance);
00154   delete this->engine;
00155   this->instance = NULL;
00156   this->engine = NULL;
00157 }
00158 
00159 void ScriptInstance::GameLoop()
00160 {
00161   ScriptObject::ActiveInstance active(this);
00162 
00163   if (this->IsDead()) return;
00164   if (this->engine->HasScriptCrashed()) {
00165     /* The script crashed during saving, kill it here. */
00166     this->Died();
00167     return;
00168   }
00169   if (this->is_paused) return;
00170   this->controller->ticks++;
00171 
00172   if (this->suspend   < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
00173   if (this->suspend   < 0)  return;          // Multiplayer suspend, wait for Continue().
00174   if (--this->suspend > 0)  return;          // Singleplayer suspend, decrease to 0.
00175 
00176   _current_company = ScriptObject::GetCompany();
00177 
00178   /* If there is a callback to call, call that first */
00179   if (this->callback != NULL) {
00180     if (this->is_save_data_on_stack) {
00181       sq_poptop(this->engine->GetVM());
00182       this->is_save_data_on_stack = false;
00183     }
00184     try {
00185       this->callback(this);
00186     } catch (Script_Suspend e) {
00187       this->suspend  = e.GetSuspendTime();
00188       this->callback = e.GetSuspendCallback();
00189 
00190       return;
00191     }
00192   }
00193 
00194   this->suspend  = 0;
00195   this->callback = NULL;
00196 
00197   if (!this->is_started) {
00198     try {
00199       ScriptObject::SetAllowDoCommand(false);
00200       /* Run the constructor if it exists. Don't allow any DoCommands in it. */
00201       if (this->engine->MethodExists(*this->instance, "constructor")) {
00202         if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
00203           if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to initialize. Script is not started.");
00204           this->Died();
00205           return;
00206         }
00207       }
00208       if (!this->CallLoad() || this->engine->IsSuspended()) {
00209         if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long in the Load function. Script is not started.");
00210         this->Died();
00211         return;
00212       }
00213       ScriptObject::SetAllowDoCommand(true);
00214       /* Start the script by calling Start() */
00215       if (!this->engine->CallMethod(*this->instance, "Start",  _settings_game.script.script_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
00216     } catch (Script_Suspend e) {
00217       this->suspend  = e.GetSuspendTime();
00218       this->callback = e.GetSuspendCallback();
00219     } catch (Script_FatalError e) {
00220       this->is_dead = true;
00221       this->engine->ThrowError(e.GetErrorMessage());
00222       this->engine->ResumeError();
00223       this->Died();
00224     }
00225 
00226     this->is_started = true;
00227     return;
00228   }
00229   if (this->is_save_data_on_stack) {
00230     sq_poptop(this->engine->GetVM());
00231     this->is_save_data_on_stack = false;
00232   }
00233 
00234   /* Continue the VM */
00235   try {
00236     if (!this->engine->Resume(_settings_game.script.script_max_opcode_till_suspend)) this->Died();
00237   } catch (Script_Suspend e) {
00238     this->suspend  = e.GetSuspendTime();
00239     this->callback = e.GetSuspendCallback();
00240   } catch (Script_FatalError e) {
00241     this->is_dead = true;
00242     this->engine->ThrowError(e.GetErrorMessage());
00243     this->engine->ResumeError();
00244     this->Died();
00245   }
00246 }
00247 
00248 void ScriptInstance::CollectGarbage() const
00249 {
00250   if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
00251 }
00252 
00253 /* static */ void ScriptInstance::DoCommandReturn(ScriptInstance *instance)
00254 {
00255   instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
00256 }
00257 
00258 /* static */ void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance *instance)
00259 {
00260   instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
00261 }
00262 
00263 /* static */ void ScriptInstance::DoCommandReturnSignID(ScriptInstance *instance)
00264 {
00265   instance->engine->InsertResult(ScriptObject::GetNewSignID());
00266 }
00267 
00268 /* static */ void ScriptInstance::DoCommandReturnGroupID(ScriptInstance *instance)
00269 {
00270   instance->engine->InsertResult(ScriptObject::GetNewGroupID());
00271 }
00272 
00273 /* static */ void ScriptInstance::DoCommandReturnGoalID(ScriptInstance *instance)
00274 {
00275   instance->engine->InsertResult(ScriptObject::GetNewGoalID());
00276 }
00277 
00278 ScriptStorage *ScriptInstance::GetStorage()
00279 {
00280   return this->storage;
00281 }
00282 
00283 void *ScriptInstance::GetLogPointer()
00284 {
00285   ScriptObject::ActiveInstance active(this);
00286 
00287   return ScriptObject::GetLogPointer();
00288 }
00289 
00290 /*
00291  * All data is stored in the following format:
00292  * First 1 byte indicating if there is a data blob at all.
00293  * 1 byte indicating the type of data.
00294  * The data itself, this differs per type:
00295  *  - integer: a binary representation of the integer (int32).
00296  *  - string:  First one byte with the string length, then a 0-terminated char
00297  *             array. The string can't be longer than 255 bytes (including
00298  *             terminating '\0').
00299  *  - array:   All data-elements of the array are saved recursive in this
00300  *             format, and ended with an element of the type
00301  *             SQSL_ARRAY_TABLE_END.
00302  *  - table:   All key/value pairs are saved in this format (first key 1, then
00303  *             value 1, then key 2, etc.). All keys and values can have an
00304  *             arbitrary type (as long as it is supported by the save function
00305  *             of course). The table is ended with an element of the type
00306  *             SQSL_ARRAY_TABLE_END.
00307  *  - bool:    A single byte with value 1 representing true and 0 false.
00308  *  - null:    No data.
00309  */
00310 
00312 enum SQSaveLoadType {
00313   SQSL_INT             = 0x00, 
00314   SQSL_STRING          = 0x01, 
00315   SQSL_ARRAY           = 0x02, 
00316   SQSL_TABLE           = 0x03, 
00317   SQSL_BOOL            = 0x04, 
00318   SQSL_NULL            = 0x05, 
00319   SQSL_ARRAY_TABLE_END = 0xFF, 
00320 };
00321 
00322 static byte _script_sl_byte; 
00323 
00325 static const SaveLoad _script_byte[] = {
00326   SLEG_VAR(_script_sl_byte, SLE_UINT8),
00327   SLE_END()
00328 };
00329 
00330 /* static */ bool ScriptInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
00331 {
00332   if (max_depth == 0) {
00333     ScriptLog::Error("Savedata can only be nested to 25 deep. No data saved."); // SQUIRREL_MAX_DEPTH = 25
00334     return false;
00335   }
00336 
00337   switch (sq_gettype(vm, index)) {
00338     case OT_INTEGER: {
00339       if (!test) {
00340         _script_sl_byte = SQSL_INT;
00341         SlObject(NULL, _script_byte);
00342       }
00343       SQInteger res;
00344       sq_getinteger(vm, index, &res);
00345       if (!test) {
00346         int value = (int)res;
00347         SlArray(&value, 1, SLE_INT32);
00348       }
00349       return true;
00350     }
00351 
00352     case OT_STRING: {
00353       if (!test) {
00354         _script_sl_byte = SQSL_STRING;
00355         SlObject(NULL, _script_byte);
00356       }
00357       const SQChar *res;
00358       sq_getstring(vm, index, &res);
00359       /* @bug if a string longer than 512 characters is given to SQ2OTTD, the
00360        *  internal buffer overflows. */
00361       const char *buf = SQ2OTTD(res);
00362       size_t len = strlen(buf) + 1;
00363       if (len >= 255) {
00364         ScriptLog::Error("Maximum string length is 254 chars. No data saved.");
00365         return false;
00366       }
00367       if (!test) {
00368         _script_sl_byte = (byte)len;
00369         SlObject(NULL, _script_byte);
00370         SlArray(const_cast<char *>(buf), len, SLE_CHAR);
00371       }
00372       return true;
00373     }
00374 
00375     case OT_ARRAY: {
00376       if (!test) {
00377         _script_sl_byte = SQSL_ARRAY;
00378         SlObject(NULL, _script_byte);
00379       }
00380       sq_pushnull(vm);
00381       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00382         /* Store the value */
00383         bool res = 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_TABLE: {
00399       if (!test) {
00400         _script_sl_byte = SQSL_TABLE;
00401         SlObject(NULL, _script_byte);
00402       }
00403       sq_pushnull(vm);
00404       while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00405         /* Store the key + value */
00406         bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
00407         sq_pop(vm, 2);
00408         if (!res) {
00409           sq_pop(vm, 1);
00410           return false;
00411         }
00412       }
00413       sq_pop(vm, 1);
00414       if (!test) {
00415         _script_sl_byte = SQSL_ARRAY_TABLE_END;
00416         SlObject(NULL, _script_byte);
00417       }
00418       return true;
00419     }
00420 
00421     case OT_BOOL: {
00422       if (!test) {
00423         _script_sl_byte = SQSL_BOOL;
00424         SlObject(NULL, _script_byte);
00425       }
00426       SQBool res;
00427       sq_getbool(vm, index, &res);
00428       if (!test) {
00429         _script_sl_byte = res ? 1 : 0;
00430         SlObject(NULL, _script_byte);
00431       }
00432       return true;
00433     }
00434 
00435     case OT_NULL: {
00436       if (!test) {
00437         _script_sl_byte = SQSL_NULL;
00438         SlObject(NULL, _script_byte);
00439       }
00440       return true;
00441     }
00442 
00443     default:
00444       ScriptLog::Error("You tried to save an unsupported type. No data saved.");
00445       return false;
00446   }
00447 }
00448 
00449 /* static */ void ScriptInstance::SaveEmpty()
00450 {
00451   _script_sl_byte = 0;
00452   SlObject(NULL, _script_byte);
00453 }
00454 
00455 void ScriptInstance::Save()
00456 {
00457   ScriptObject::ActiveInstance active(this);
00458 
00459   /* Don't save data if the script didn't start yet or if it crashed. */
00460   if (this->engine == NULL || this->engine->HasScriptCrashed()) {
00461     SaveEmpty();
00462     return;
00463   }
00464 
00465   HSQUIRRELVM vm = this->engine->GetVM();
00466   if (this->is_save_data_on_stack) {
00467     _script_sl_byte = 1;
00468     SlObject(NULL, _script_byte);
00469     /* Save the data that was just loaded. */
00470     SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
00471   } else if (!this->is_started) {
00472     SaveEmpty();
00473     return;
00474   } else if (this->engine->MethodExists(*this->instance, "Save")) {
00475     HSQOBJECT savedata;
00476     /* We don't want to be interrupted during the save function. */
00477     bool backup_allow = ScriptObject::GetAllowDoCommand();
00478     ScriptObject::SetAllowDoCommand(false);
00479     try {
00480       if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
00481         /* The script crashed in the Save function. We can't kill
00482          * it here, but do so in the next script tick. */
00483         SaveEmpty();
00484         this->engine->CrashOccurred();
00485         return;
00486       }
00487     } catch (Script_FatalError e) {
00488       /* If we don't mark the script as dead here cleaning up the squirrel
00489        * stack could throw Script_FatalError again. */
00490       this->is_dead = true;
00491       this->engine->ThrowError(e.GetErrorMessage());
00492       this->engine->ResumeError();
00493       SaveEmpty();
00494       /* We can't kill the script here, so mark it as crashed (not dead) and
00495        * kill it in the next script tick. */
00496       this->is_dead = false;
00497       this->engine->CrashOccurred();
00498       return;
00499     }
00500     ScriptObject::SetAllowDoCommand(backup_allow);
00501 
00502     if (!sq_istable(savedata)) {
00503       ScriptLog::Error(this->engine->IsSuspended() ? "This script took too long to Save." : "Save function should return a table.");
00504       SaveEmpty();
00505       this->engine->CrashOccurred();
00506       return;
00507     }
00508     sq_pushobject(vm, savedata);
00509     if (SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, true)) {
00510       _script_sl_byte = 1;
00511       SlObject(NULL, _script_byte);
00512       SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
00513       this->is_save_data_on_stack = true;
00514     } else {
00515       SaveEmpty();
00516       this->engine->CrashOccurred();
00517     }
00518   } else {
00519     ScriptLog::Warning("Save function is not implemented");
00520     _script_sl_byte = 0;
00521     SlObject(NULL, _script_byte);
00522   }
00523 }
00524 
00525 void ScriptInstance::Pause()
00526 {
00527   /* Suspend script. */
00528   HSQUIRRELVM vm = this->engine->GetVM();
00529   Squirrel::DecreaseOps(vm, _settings_game.script.script_max_opcode_till_suspend);
00530 
00531   this->is_paused = true;
00532 }
00533 
00534 void ScriptInstance::Unpause()
00535 {
00536   this->is_paused = false;
00537 }
00538 
00539 bool ScriptInstance::IsPaused()
00540 {
00541   return this->is_paused;
00542 }
00543 
00544 /* static */ bool ScriptInstance::LoadObjects(HSQUIRRELVM vm)
00545 {
00546   SlObject(NULL, _script_byte);
00547   switch (_script_sl_byte) {
00548     case SQSL_INT: {
00549       int value;
00550       SlArray(&value, 1, SLE_INT32);
00551       if (vm != NULL) sq_pushinteger(vm, (SQInteger)value);
00552       return true;
00553     }
00554 
00555     case SQSL_STRING: {
00556       SlObject(NULL, _script_byte);
00557       static char buf[256];
00558       SlArray(buf, _script_sl_byte, SLE_CHAR);
00559       if (vm != NULL) sq_pushstring(vm, OTTD2SQ(buf), -1);
00560       return true;
00561     }
00562 
00563     case SQSL_ARRAY: {
00564       if (vm != NULL) sq_newarray(vm, 0);
00565       while (LoadObjects(vm)) {
00566         if (vm != NULL) sq_arrayappend(vm, -2);
00567         /* The value is popped from the stack by squirrel. */
00568       }
00569       return true;
00570     }
00571 
00572     case SQSL_TABLE: {
00573       if (vm != NULL) sq_newtable(vm);
00574       while (LoadObjects(vm)) {
00575         LoadObjects(vm);
00576         if (vm != NULL) sq_rawset(vm, -3);
00577         /* The key (-2) and value (-1) are popped from the stack by squirrel. */
00578       }
00579       return true;
00580     }
00581 
00582     case SQSL_BOOL: {
00583       SlObject(NULL, _script_byte);
00584       if (vm != NULL) sq_pushinteger(vm, (SQBool)(_script_sl_byte != 0));
00585       return true;
00586     }
00587 
00588     case SQSL_NULL: {
00589       if (vm != NULL) sq_pushnull(vm);
00590       return true;
00591     }
00592 
00593     case SQSL_ARRAY_TABLE_END: {
00594       return false;
00595     }
00596 
00597     default: NOT_REACHED();
00598   }
00599 }
00600 
00601 /* static */ void ScriptInstance::LoadEmpty()
00602 {
00603   SlObject(NULL, _script_byte);
00604   /* Check if there was anything saved at all. */
00605   if (_script_sl_byte == 0) return;
00606 
00607   LoadObjects(NULL);
00608 }
00609 
00610 void ScriptInstance::Load(int version)
00611 {
00612   ScriptObject::ActiveInstance active(this);
00613 
00614   if (this->engine == NULL || version == -1) {
00615     LoadEmpty();
00616     return;
00617   }
00618   HSQUIRRELVM vm = this->engine->GetVM();
00619 
00620   SlObject(NULL, _script_byte);
00621   /* Check if there was anything saved at all. */
00622   if (_script_sl_byte == 0) return;
00623 
00624   sq_pushinteger(vm, version);
00625   LoadObjects(vm);
00626   this->is_save_data_on_stack = true;
00627 }
00628 
00629 bool ScriptInstance::CallLoad()
00630 {
00631   HSQUIRRELVM vm = this->engine->GetVM();
00632   /* Is there save data that we should load? */
00633   if (!this->is_save_data_on_stack) return true;
00634   /* Whatever happens, after CallLoad the savegame data is removed from the stack. */
00635   this->is_save_data_on_stack = false;
00636 
00637   if (!this->engine->MethodExists(*this->instance, "Load")) {
00638     ScriptLog::Warning("Loading failed: there was data for the script to load, but the script does not have a Load() function.");
00639 
00640     /* Pop the savegame data and version. */
00641     sq_pop(vm, 2);
00642     return true;
00643   }
00644 
00645   /* Go to the instance-root */
00646   sq_pushobject(vm, *this->instance);
00647   /* Find the function-name inside the script */
00648   sq_pushstring(vm, OTTD2SQ("Load"), -1);
00649   /* Change the "Load" string in a function pointer */
00650   sq_get(vm, -2);
00651   /* Push the main instance as "this" object */
00652   sq_pushobject(vm, *this->instance);
00653   /* Push the version data and savegame data as arguments */
00654   sq_push(vm, -5);
00655   sq_push(vm, -5);
00656 
00657   /* Call the script load function. sq_call removes the arguments (but not the
00658    * function pointer) from the stack. */
00659   if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
00660 
00661   /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
00662   sq_pop(vm, 4);
00663   return true;
00664 }
00665 
00666 SQInteger ScriptInstance::GetOpsTillSuspend()
00667 {
00668   return this->engine->GetOpsTillSuspend();
00669 }
00670 
00671 void ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
00672 {
00673   ScriptObject::ActiveInstance active(this);
00674 
00675   ScriptObject::SetLastCommandRes(result.Succeeded());
00676 
00677   if (result.Failed()) {
00678     ScriptObject::SetLastError(ScriptError::StringToError(result.GetErrorMessage()));
00679   } else {
00680     ScriptObject::IncreaseDoCommandCosts(result.GetCost());
00681     ScriptObject::SetLastCost(result.GetCost());
00682   }
00683 }
00684 
00685 void ScriptInstance::InsertEvent(class ScriptEvent *event)
00686 {
00687   ScriptObject::ActiveInstance active(this);
00688 
00689   ScriptEventController::InsertEvent(event);
00690 }