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 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
00073 this->engine->SetGlobalPointer(this->engine);
00074 this->RegisterAPI();
00075
00076 try {
00077 ScriptObject::SetAllowDoCommand(false);
00078
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
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
00165 this->Died();
00166 return;
00167 }
00168 this->controller->ticks++;
00169
00170 if (this->suspend < -1) this->suspend++;
00171 if (this->suspend < 0) return;
00172 if (--this->suspend > 0) return;
00173
00174 _current_company = ScriptObject::GetCompany();
00175
00176
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
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
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
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 void ScriptInstance::DoCommandReturn(ScriptInstance *instance)
00252 {
00253 instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
00254 }
00255
00256 void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance *instance)
00257 {
00258 instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
00259 }
00260
00261 void ScriptInstance::DoCommandReturnSignID(ScriptInstance *instance)
00262 {
00263 instance->engine->InsertResult(ScriptObject::GetNewSignID());
00264 }
00265
00266 void ScriptInstance::DoCommandReturnGroupID(ScriptInstance *instance)
00267 {
00268 instance->engine->InsertResult(ScriptObject::GetNewGroupID());
00269 }
00270
00271 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
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
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 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.");
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
00358
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
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
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 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
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
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
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
00480
00481 SaveEmpty();
00482 this->engine->CrashOccurred();
00483 return;
00484 }
00485 } catch (Script_FatalError e) {
00486
00487
00488 this->is_dead = true;
00489 this->engine->ThrowError(e.GetErrorMessage());
00490 this->engine->ResumeError();
00491 SaveEmpty();
00492
00493
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 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
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
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 void ScriptInstance::LoadEmpty()
00587 {
00588 SlObject(NULL, _script_byte);
00589
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
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
00618 if (!this->is_save_data_on_stack) return true;
00619
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
00626 sq_pop(vm, 2);
00627 return true;
00628 }
00629
00630
00631 sq_pushobject(vm, *this->instance);
00632
00633 sq_pushstring(vm, OTTD2SQ("Load"), -1);
00634
00635 sq_get(vm, -2);
00636
00637 sq_pushobject(vm, *this->instance);
00638
00639 sq_push(vm, -5);
00640 sq_push(vm, -5);
00641
00642
00643
00644 if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
00645
00646
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 }