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