00001
00002
00003
00004
00005
00006
00007
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
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
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
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 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
00166 this->Died();
00167 return;
00168 }
00169 if (this->is_paused) return;
00170 this->controller->ticks++;
00171
00172 if (this->suspend < -1) this->suspend++;
00173 if (this->suspend < 0) return;
00174 if (--this->suspend > 0) return;
00175
00176 _current_company = ScriptObject::GetCompany();
00177
00178
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
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
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
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 void ScriptInstance::DoCommandReturn(ScriptInstance *instance)
00254 {
00255 instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
00256 }
00257
00258 void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance *instance)
00259 {
00260 instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
00261 }
00262
00263 void ScriptInstance::DoCommandReturnSignID(ScriptInstance *instance)
00264 {
00265 instance->engine->InsertResult(ScriptObject::GetNewSignID());
00266 }
00267
00268 void ScriptInstance::DoCommandReturnGroupID(ScriptInstance *instance)
00269 {
00270 instance->engine->InsertResult(ScriptObject::GetNewGroupID());
00271 }
00272
00273 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
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
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 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.");
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
00360
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
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
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 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
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
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
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
00482
00483 SaveEmpty();
00484 this->engine->CrashOccurred();
00485 return;
00486 }
00487 } catch (Script_FatalError e) {
00488
00489
00490 this->is_dead = true;
00491 this->engine->ThrowError(e.GetErrorMessage());
00492 this->engine->ResumeError();
00493 SaveEmpty();
00494
00495
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
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 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
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
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 void ScriptInstance::LoadEmpty()
00602 {
00603 SlObject(NULL, _script_byte);
00604
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
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
00633 if (!this->is_save_data_on_stack) return true;
00634
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
00641 sq_pop(vm, 2);
00642 return true;
00643 }
00644
00645
00646 sq_pushobject(vm, *this->instance);
00647
00648 sq_pushstring(vm, OTTD2SQ("Load"), -1);
00649
00650 sq_get(vm, -2);
00651
00652 sq_pushobject(vm, *this->instance);
00653
00654 sq_push(vm, -5);
00655 sq_push(vm, -5);
00656
00657
00658
00659 if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
00660
00661
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 }