00001
00002
00003
00004
00005
00006
00007
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
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
00048 ScriptController::Print(error_msg, SQ2OTTD(message));
00049 }
00050
00051 ScriptInstance::ScriptInstance(const char *APIName) :
00052 engine(NULL),
00053 controller(NULL),
00054 storage(NULL),
00055 instance(NULL),
00056 is_started(false),
00057 is_dead(false),
00058 is_save_data_on_stack(false),
00059 suspend(0),
00060 callback(NULL)
00061 {
00062 this->storage = new ScriptStorage();
00063 this->engine = new Squirrel(APIName);
00064 this->engine->SetPrintFunction(&PrintFunc);
00065 }
00066
00067 void ScriptInstance::Initialize(const char *main_script, const char *instance_name, CompanyID company)
00068 {
00069 ScriptObject::ActiveInstance active(this);
00070
00071 this->controller = new ScriptController(company);
00072
00073
00074 this->engine->SetGlobalPointer(this->engine);
00075 this->RegisterAPI();
00076
00077 try {
00078 ScriptObject::SetAllowDoCommand(false);
00079
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
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
00144 this->Died();
00145 return;
00146 }
00147 this->controller->ticks++;
00148
00149 if (this->suspend < -1) this->suspend++;
00150 if (this->suspend < 0) return;
00151 if (--this->suspend > 0) return;
00152
00153 _current_company = ScriptObject::GetCompany();
00154
00155
00156 if (this->callback != NULL) {
00157 if (this->is_save_data_on_stack) {
00158 sq_poptop(this->engine->GetVM());
00159 this->is_save_data_on_stack = false;
00160 }
00161 try {
00162 this->callback(this);
00163 } catch (Script_Suspend e) {
00164 this->suspend = e.GetSuspendTime();
00165 this->callback = e.GetSuspendCallback();
00166
00167 return;
00168 }
00169 }
00170
00171 this->suspend = 0;
00172 this->callback = NULL;
00173
00174 if (!this->is_started) {
00175 try {
00176 ScriptObject::SetAllowDoCommand(false);
00177
00178 if (this->engine->MethodExists(*this->instance, "constructor")) {
00179 if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
00180 if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to initialize. Script is not started.");
00181 this->Died();
00182 return;
00183 }
00184 }
00185 if (!this->CallLoad() || this->engine->IsSuspended()) {
00186 if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long in the Load function. Script is not started.");
00187 this->Died();
00188 return;
00189 }
00190 ScriptObject::SetAllowDoCommand(true);
00191
00192 if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.script.script_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
00193 } catch (Script_Suspend e) {
00194 this->suspend = e.GetSuspendTime();
00195 this->callback = e.GetSuspendCallback();
00196 } catch (Script_FatalError e) {
00197 this->is_dead = true;
00198 this->engine->ThrowError(e.GetErrorMessage());
00199 this->engine->ResumeError();
00200 this->Died();
00201 }
00202
00203 this->is_started = true;
00204 return;
00205 }
00206 if (this->is_save_data_on_stack) {
00207 sq_poptop(this->engine->GetVM());
00208 this->is_save_data_on_stack = false;
00209 }
00210
00211
00212 try {
00213 if (!this->engine->Resume(_settings_game.script.script_max_opcode_till_suspend)) this->Died();
00214 } catch (Script_Suspend e) {
00215 this->suspend = e.GetSuspendTime();
00216 this->callback = e.GetSuspendCallback();
00217 } catch (Script_FatalError e) {
00218 this->is_dead = true;
00219 this->engine->ThrowError(e.GetErrorMessage());
00220 this->engine->ResumeError();
00221 this->Died();
00222 }
00223 }
00224
00225 void ScriptInstance::CollectGarbage() const
00226 {
00227 if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
00228 }
00229
00230 void ScriptInstance::DoCommandReturn(ScriptInstance *instance)
00231 {
00232 instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
00233 }
00234
00235 void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance *instance)
00236 {
00237 instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
00238 }
00239
00240 void ScriptInstance::DoCommandReturnSignID(ScriptInstance *instance)
00241 {
00242 instance->engine->InsertResult(ScriptObject::GetNewSignID());
00243 }
00244
00245 void ScriptInstance::DoCommandReturnGroupID(ScriptInstance *instance)
00246 {
00247 instance->engine->InsertResult(ScriptObject::GetNewGroupID());
00248 }
00249
00250 void ScriptInstance::DoCommandReturnGoalID(ScriptInstance *instance)
00251 {
00252 instance->engine->InsertResult(ScriptObject::GetNewGoalID());
00253 }
00254
00255 ScriptStorage *ScriptInstance::GetStorage()
00256 {
00257 return this->storage;
00258 }
00259
00260 void *ScriptInstance::GetLogPointer()
00261 {
00262 ScriptObject::ActiveInstance active(this);
00263
00264 return ScriptObject::GetLogPointer();
00265 }
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00289 enum SQSaveLoadType {
00290 SQSL_INT = 0x00,
00291 SQSL_STRING = 0x01,
00292 SQSL_ARRAY = 0x02,
00293 SQSL_TABLE = 0x03,
00294 SQSL_BOOL = 0x04,
00295 SQSL_NULL = 0x05,
00296 SQSL_ARRAY_TABLE_END = 0xFF,
00297 };
00298
00299 static byte _script_sl_byte;
00300
00302 static const SaveLoad _script_byte[] = {
00303 SLEG_VAR(_script_sl_byte, SLE_UINT8),
00304 SLE_END()
00305 };
00306
00307 bool ScriptInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
00308 {
00309 if (max_depth == 0) {
00310 ScriptLog::Error("Savedata can only be nested to 25 deep. No data saved.");
00311 return false;
00312 }
00313
00314 switch (sq_gettype(vm, index)) {
00315 case OT_INTEGER: {
00316 if (!test) {
00317 _script_sl_byte = SQSL_INT;
00318 SlObject(NULL, _script_byte);
00319 }
00320 SQInteger res;
00321 sq_getinteger(vm, index, &res);
00322 if (!test) {
00323 int value = (int)res;
00324 SlArray(&value, 1, SLE_INT32);
00325 }
00326 return true;
00327 }
00328
00329 case OT_STRING: {
00330 if (!test) {
00331 _script_sl_byte = SQSL_STRING;
00332 SlObject(NULL, _script_byte);
00333 }
00334 const SQChar *res;
00335 sq_getstring(vm, index, &res);
00336
00337
00338 const char *buf = SQ2OTTD(res);
00339 size_t len = strlen(buf) + 1;
00340 if (len >= 255) {
00341 ScriptLog::Error("Maximum string length is 254 chars. No data saved.");
00342 return false;
00343 }
00344 if (!test) {
00345 _script_sl_byte = (byte)len;
00346 SlObject(NULL, _script_byte);
00347 SlArray(const_cast<char *>(buf), len, SLE_CHAR);
00348 }
00349 return true;
00350 }
00351
00352 case OT_ARRAY: {
00353 if (!test) {
00354 _script_sl_byte = SQSL_ARRAY;
00355 SlObject(NULL, _script_byte);
00356 }
00357 sq_pushnull(vm);
00358 while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00359
00360 bool res = SaveObject(vm, -1, max_depth - 1, test);
00361 sq_pop(vm, 2);
00362 if (!res) {
00363 sq_pop(vm, 1);
00364 return false;
00365 }
00366 }
00367 sq_pop(vm, 1);
00368 if (!test) {
00369 _script_sl_byte = SQSL_ARRAY_TABLE_END;
00370 SlObject(NULL, _script_byte);
00371 }
00372 return true;
00373 }
00374
00375 case OT_TABLE: {
00376 if (!test) {
00377 _script_sl_byte = SQSL_TABLE;
00378 SlObject(NULL, _script_byte);
00379 }
00380 sq_pushnull(vm);
00381 while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
00382
00383 bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
00384 sq_pop(vm, 2);
00385 if (!res) {
00386 sq_pop(vm, 1);
00387 return false;
00388 }
00389 }
00390 sq_pop(vm, 1);
00391 if (!test) {
00392 _script_sl_byte = SQSL_ARRAY_TABLE_END;
00393 SlObject(NULL, _script_byte);
00394 }
00395 return true;
00396 }
00397
00398 case OT_BOOL: {
00399 if (!test) {
00400 _script_sl_byte = SQSL_BOOL;
00401 SlObject(NULL, _script_byte);
00402 }
00403 SQBool res;
00404 sq_getbool(vm, index, &res);
00405 if (!test) {
00406 _script_sl_byte = res ? 1 : 0;
00407 SlObject(NULL, _script_byte);
00408 }
00409 return true;
00410 }
00411
00412 case OT_NULL: {
00413 if (!test) {
00414 _script_sl_byte = SQSL_NULL;
00415 SlObject(NULL, _script_byte);
00416 }
00417 return true;
00418 }
00419
00420 default:
00421 ScriptLog::Error("You tried to save an unsupported type. No data saved.");
00422 return false;
00423 }
00424 }
00425
00426 void ScriptInstance::SaveEmpty()
00427 {
00428 _script_sl_byte = 0;
00429 SlObject(NULL, _script_byte);
00430 }
00431
00432 void ScriptInstance::Save()
00433 {
00434 ScriptObject::ActiveInstance active(this);
00435
00436
00437 if (this->engine == NULL || this->engine->HasScriptCrashed()) {
00438 SaveEmpty();
00439 return;
00440 }
00441
00442 HSQUIRRELVM vm = this->engine->GetVM();
00443 if (this->is_save_data_on_stack) {
00444 _script_sl_byte = 1;
00445 SlObject(NULL, _script_byte);
00446
00447 SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
00448 } else if (!this->is_started) {
00449 SaveEmpty();
00450 return;
00451 } else if (this->engine->MethodExists(*this->instance, "Save")) {
00452 HSQOBJECT savedata;
00453
00454 bool backup_allow = ScriptObject::GetAllowDoCommand();
00455 ScriptObject::SetAllowDoCommand(false);
00456 try {
00457 if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
00458
00459
00460 SaveEmpty();
00461 this->engine->CrashOccurred();
00462 return;
00463 }
00464 } catch (Script_FatalError e) {
00465
00466
00467 this->is_dead = true;
00468 this->engine->ThrowError(e.GetErrorMessage());
00469 this->engine->ResumeError();
00470 SaveEmpty();
00471
00472
00473 this->is_dead = false;
00474 this->engine->CrashOccurred();
00475 return;
00476 }
00477 ScriptObject::SetAllowDoCommand(backup_allow);
00478
00479 if (!sq_istable(savedata)) {
00480 ScriptLog::Error(this->engine->IsSuspended() ? "This script took too long to Save." : "Save function should return a table.");
00481 SaveEmpty();
00482 this->engine->CrashOccurred();
00483 return;
00484 }
00485 sq_pushobject(vm, savedata);
00486 if (SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, true)) {
00487 _script_sl_byte = 1;
00488 SlObject(NULL, _script_byte);
00489 SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
00490 this->is_save_data_on_stack = true;
00491 } else {
00492 SaveEmpty();
00493 this->engine->CrashOccurred();
00494 }
00495 } else {
00496 ScriptLog::Warning("Save function is not implemented");
00497 _script_sl_byte = 0;
00498 SlObject(NULL, _script_byte);
00499 }
00500 }
00501
00502 void ScriptInstance::Suspend()
00503 {
00504 HSQUIRRELVM vm = this->engine->GetVM();
00505 Squirrel::DecreaseOps(vm, _settings_game.script.script_max_opcode_till_suspend);
00506 }
00507
00508 bool ScriptInstance::LoadObjects(HSQUIRRELVM vm)
00509 {
00510 SlObject(NULL, _script_byte);
00511 switch (_script_sl_byte) {
00512 case SQSL_INT: {
00513 int value;
00514 SlArray(&value, 1, SLE_INT32);
00515 if (vm != NULL) sq_pushinteger(vm, (SQInteger)value);
00516 return true;
00517 }
00518
00519 case SQSL_STRING: {
00520 SlObject(NULL, _script_byte);
00521 static char buf[256];
00522 SlArray(buf, _script_sl_byte, SLE_CHAR);
00523 if (vm != NULL) sq_pushstring(vm, OTTD2SQ(buf), -1);
00524 return true;
00525 }
00526
00527 case SQSL_ARRAY: {
00528 if (vm != NULL) sq_newarray(vm, 0);
00529 while (LoadObjects(vm)) {
00530 if (vm != NULL) sq_arrayappend(vm, -2);
00531
00532 }
00533 return true;
00534 }
00535
00536 case SQSL_TABLE: {
00537 if (vm != NULL) sq_newtable(vm);
00538 while (LoadObjects(vm)) {
00539 LoadObjects(vm);
00540 if (vm != NULL) sq_rawset(vm, -3);
00541
00542 }
00543 return true;
00544 }
00545
00546 case SQSL_BOOL: {
00547 SlObject(NULL, _script_byte);
00548 if (vm != NULL) sq_pushinteger(vm, (SQBool)(_script_sl_byte != 0));
00549 return true;
00550 }
00551
00552 case SQSL_NULL: {
00553 if (vm != NULL) sq_pushnull(vm);
00554 return true;
00555 }
00556
00557 case SQSL_ARRAY_TABLE_END: {
00558 return false;
00559 }
00560
00561 default: NOT_REACHED();
00562 }
00563 }
00564
00565 void ScriptInstance::LoadEmpty()
00566 {
00567 SlObject(NULL, _script_byte);
00568
00569 if (_script_sl_byte == 0) return;
00570
00571 LoadObjects(NULL);
00572 }
00573
00574 void ScriptInstance::Load(int version)
00575 {
00576 ScriptObject::ActiveInstance active(this);
00577
00578 if (this->engine == NULL || version == -1) {
00579 LoadEmpty();
00580 return;
00581 }
00582 HSQUIRRELVM vm = this->engine->GetVM();
00583
00584 SlObject(NULL, _script_byte);
00585
00586 if (_script_sl_byte == 0) return;
00587
00588 sq_pushinteger(vm, version);
00589 LoadObjects(vm);
00590 this->is_save_data_on_stack = true;
00591 }
00592
00593 bool ScriptInstance::CallLoad()
00594 {
00595 HSQUIRRELVM vm = this->engine->GetVM();
00596
00597 if (!this->is_save_data_on_stack) return true;
00598
00599 this->is_save_data_on_stack = false;
00600
00601 if (!this->engine->MethodExists(*this->instance, "Load")) {
00602 ScriptLog::Warning("Loading failed: there was data for the script to load, but the script does not have a Load() function.");
00603
00604
00605 sq_pop(vm, 2);
00606 return true;
00607 }
00608
00609
00610 sq_pushobject(vm, *this->instance);
00611
00612 sq_pushstring(vm, OTTD2SQ("Load"), -1);
00613
00614 sq_get(vm, -2);
00615
00616 sq_pushobject(vm, *this->instance);
00617
00618 sq_push(vm, -5);
00619 sq_push(vm, -5);
00620
00621
00622
00623 if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
00624
00625
00626 sq_pop(vm, 4);
00627 return true;
00628 }
00629
00630 SQInteger ScriptInstance::GetOpsTillSuspend()
00631 {
00632 return this->engine->GetOpsTillSuspend();
00633 }
00634
00635 void ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
00636 {
00637 ScriptObject::ActiveInstance active(this);
00638
00639 ScriptObject::SetLastCommandRes(result.Succeeded());
00640
00641 if (result.Failed()) {
00642 ScriptObject::SetLastError(ScriptError::StringToError(result.GetErrorMessage()));
00643 } else {
00644 ScriptObject::IncreaseDoCommandCosts(result.GetCost());
00645 ScriptObject::SetLastCost(result.GetCost());
00646 }
00647 }
00648
00649 void ScriptInstance::InsertEvent(class ScriptEvent *event)
00650 {
00651 ScriptObject::ActiveInstance active(this);
00652
00653 ScriptEventController::InsertEvent(event);
00654 }