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