OpenTTD Source: src/script/script_instance.cpp Source File

OpenTTD Source 12.0-beta1

script_instance.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
7
10 #include "../stdafx.h"
11 #include "../debug.h"
12 #include "../saveload/saveload.h"
13
14 #include "../script/squirrel_class.hpp"
15
16 #include "script_fatalerror.hpp"
17 #include "script_storage.hpp"
18 #include "script_info.hpp"
19 #include "script_instance.hpp"
20
21 #include "api/script_controller.hpp"
22 #include "api/script_error.hpp"
23 #include "api/script_event.hpp"
24 #include "api/script_log.hpp"
25
26 #include "../company_base.h"
27 #include "../company_func.h"
28 #include "../fileio_func.h"
29
30 #include "../safeguards.h"
31
32 ScriptStorage::~ScriptStorage()
33 {
34 /* Free our pointers */
35 if (event_data != nullptr) ScriptEventController::FreeEventPointer();
36 if (log_data != nullptr) ScriptLog::FreeLogPointer();
37 }
38
44 static void PrintFunc(bool error_msg, const SQChar *message)
45 {
46 /* Convert to OpenTTD internal capable string */
47 ScriptController::Print(error_msg, message);
48 }
49
50 ScriptInstance::ScriptInstance(const char *APIName) :
51 engine(nullptr),
52 versionAPI(nullptr),
53 controller(nullptr),
54 storage(nullptr),
55 instance(nullptr),
56 is_started(false),
57 is_dead(false),
58 is_save_data_on_stack(false),
59 suspend(0),
60 is_paused(false),
61 in_shutdown(false),
62 callback(nullptr)
63 {
64 this->storage = new ScriptStorage();
65 this->engine = new Squirrel(APIName);
66 this->engine->SetPrintFunction(&PrintFunc);
67 }
68
69 void ScriptInstance::Initialize(const char *main_script, const char *instance_name, CompanyID company)
70 {
71 ScriptObject::ActiveInstance active(this);
72
73 this->controller = new ScriptController(company);
74
75 /* Register the API functions and classes */
76 this->engine->SetGlobalPointer(this->engine);
77 this->RegisterAPI();
78
79 try {
80 ScriptObject::SetAllowDoCommand(false);
81 /* Load and execute the script for this script */
82 if (strcmp(main_script, "%_dummy") == 0) {
83 this->LoadDummyScript();
84 } else if (!this->engine->LoadScript(main_script) || this->engine->IsSuspended()) {
85 if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to load script. AI is not started.");
86 this->Died();
87 return;
88 }
89
90 /* Create the main-class */
91 this->instance = new SQObject();
92 if (!this->engine->CreateClassInstance(instance_name, this->controller, this->instance)) {
93 /* If CreateClassInstance has returned false instance has not been
94 * registered with squirrel, so avoid trying to Release it by clearing it now */
95 delete this->instance;
96 this->instance = nullptr;
97 this->Died();
98 return;
99 }
100 ScriptObject::SetAllowDoCommand(true);
101 } catch (Script_FatalError &e) {
102 this->is_dead = true;
103 this->engine->ThrowError(e.GetErrorMessage().c_str());
104 this->engine->ResumeError();
105 this->Died();
106 }
107 }
108
109 void ScriptInstance::RegisterAPI()
110 {
111 extern void squirrel_register_std(Squirrel *engine);
112 squirrel_register_std(this->engine);
113 }
114
115 bool ScriptInstance::LoadCompatibilityScripts(const char *api_version, Subdirectory dir)
116 {
117 char script_name[32];
118 seprintf(script_name, lastof(script_name), "compat_%s.nut", api_version);
119 for (Searchpath sp : _valid_searchpaths) {
120 std::string buf = FioGetDirectory(sp, dir);
121 buf += script_name;
122 if (!FileExists(buf)) continue;
123
124 if (this->engine->LoadScript(buf.c_str())) return true;
125
126 ScriptLog::Error("Failed to load API compatibility script");
127 Debug(script, 0, "Error compiling / running API compatibility script: {}", buf);
128 return false;
129 }
130
131 ScriptLog::Warning("API compatibility script not found");
132 return true;
133 }
134
135 ScriptInstance::~ScriptInstance()
136 {
137 ScriptObject::ActiveInstance active(this);
138 this->in_shutdown = true;
139
140 if (instance != nullptr) this->engine->ReleaseObject(this->instance);
141 if (engine != nullptr) delete this->engine;
142 delete this->storage;
143 delete this->controller;
144 delete this->instance;
145 }
146
147 void ScriptInstance::Continue()
148 {
149 assert(this->suspend < 0);
150 this->suspend = -this->suspend - 1;
151 }
152
153 void ScriptInstance::Died()
154 {
155 Debug(script, 0, "The script died unexpectedly.");
156 this->is_dead = true;
157 this->in_shutdown = true;
158
159 this->last_allocated_memory = this->GetAllocatedMemory(); // Update cache
160
161 if (this->instance != nullptr) this->engine->ReleaseObject(this->instance);
162 delete this->instance;
163 delete this->engine;
164 this->instance = nullptr;
165 this->engine = nullptr;
166 }
167
168 void ScriptInstance::GameLoop()
169 {
170 ScriptObject::ActiveInstance active(this);
171
172 if (this->IsDead()) return;
173 if (this->engine->HasScriptCrashed()) {
174 /* The script crashed during saving, kill it here. */
175 this->Died();
176 return;
177 }
178 if (this->is_paused) return;
179 this->controller->ticks++;
180
181 if (this->suspend < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
182 if (this->suspend < 0) return; // Multiplayer suspend, wait for Continue().
183 if (--this->suspend > 0) return; // Singleplayer suspend, decrease to 0.
184
185 _current_company = ScriptObject::GetCompany();
186
187 /* If there is a callback to call, call that first */
188 if (this->callback != nullptr) {
189 if (this->is_save_data_on_stack) {
190 sq_poptop(this->engine->GetVM());
191 this->is_save_data_on_stack = false;
192 }
193 try {
194 this->callback(this);
195 } catch (Script_Suspend &e) {
196 this->suspend = e.GetSuspendTime();
197 this->callback = e.GetSuspendCallback();
198
199 return;
200 }
201 }
202
203 this->suspend = 0;
204 this->callback = nullptr;
205
206 if (!this->is_started) {
207 try {
208 ScriptObject::SetAllowDoCommand(false);
209 /* Run the constructor if it exists. Don't allow any DoCommands in it. */
210 if (this->engine->MethodExists(*this->instance, "constructor")) {
211 if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
212 if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to initialize. Script is not started.");
213 this->Died();
214 return;
215 }
216 }
217 if (!this->CallLoad() || this->engine->IsSuspended()) {
218 if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long in the Load function. Script is not started.");
219 this->Died();
220 return;
221 }
222 ScriptObject::SetAllowDoCommand(true);
223 /* Start the script by calling Start() */
224 if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.script.script_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
225 } catch (Script_Suspend &e) {
226 this->suspend = e.GetSuspendTime();
227 this->callback = e.GetSuspendCallback();
228 } catch (Script_FatalError &e) {
229 this->is_dead = true;
230 this->engine->ThrowError(e.GetErrorMessage().c_str());
231 this->engine->ResumeError();
232 this->Died();
233 }
234
235 this->is_started = true;
236 return;
237 }
238 if (this->is_save_data_on_stack) {
239 sq_poptop(this->engine->GetVM());
240 this->is_save_data_on_stack = false;
241 }
242
243 /* Continue the VM */
244 try {
245 if (!this->engine->Resume(_settings_game.script.script_max_opcode_till_suspend)) this->Died();
246 } catch (Script_Suspend &e) {
247 this->suspend = e.GetSuspendTime();
248 this->callback = e.GetSuspendCallback();
249 } catch (Script_FatalError &e) {
250 this->is_dead = true;
251 this->engine->ThrowError(e.GetErrorMessage().c_str());
252 this->engine->ResumeError();
253 this->Died();
254 }
255 }
256
257 void ScriptInstance::CollectGarbage()
258 {
259 if (this->is_started && !this->IsDead()) {
260 ScriptObject::ActiveInstance active(this);
261 this->engine->CollectGarbage();
262 }
263 }
264
265 /* static */ void ScriptInstance::DoCommandReturn(ScriptInstance *instance)
266 {
267 instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
268 }
269
270 /* static */ void ScriptInstance::DoCommandReturnVehicleID(ScriptInstance *instance)
271 {
272 instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
273 }
274
275 /* static */ void ScriptInstance::DoCommandReturnSignID(ScriptInstance *instance)
276 {
277 instance->engine->InsertResult(ScriptObject::GetNewSignID());
278 }
279
280 /* static */ void ScriptInstance::DoCommandReturnGroupID(ScriptInstance *instance)
281 {
282 instance->engine->InsertResult(ScriptObject::GetNewGroupID());
283 }
284
285 /* static */ void ScriptInstance::DoCommandReturnGoalID(ScriptInstance *instance)
286 {
287 instance->engine->InsertResult(ScriptObject::GetNewGoalID());
288 }
289
290 /* static */ void ScriptInstance::DoCommandReturnStoryPageID(ScriptInstance *instance)
291 {
292 instance->engine->InsertResult(ScriptObject::GetNewStoryPageID());
293 }
294
295 /* static */ void ScriptInstance::DoCommandReturnStoryPageElementID(ScriptInstance *instance)
296 {
297 instance->engine->InsertResult(ScriptObject::GetNewStoryPageElementID());
298 }
299
300 ScriptStorage *ScriptInstance::GetStorage()
301 {
302 return this->storage;
303 }
304
305 void *ScriptInstance::GetLogPointer()
306 {
307 ScriptObject::ActiveInstance active(this);
308
309 return ScriptObject::GetLogPointer();
310 }
311
312 /*
313 * All data is stored in the following format:
314 * First 1 byte indicating if there is a data blob at all.
315 * 1 byte indicating the type of data.
316 * The data itself, this differs per type:
317 * - integer: a binary representation of the integer (int32).
318 * - string: First one byte with the string length, then a 0-terminated char
319 * array. The string can't be longer than 255 bytes (including
320 * terminating '\0').
321 * - array: All data-elements of the array are saved recursive in this
322 * format, and ended with an element of the type
323 * SQSL_ARRAY_TABLE_END.
324 * - table: All key/value pairs are saved in this format (first key 1, then
325 * value 1, then key 2, etc.). All keys and values can have an
326 * arbitrary type (as long as it is supported by the save function
327 * of course). The table is ended with an element of the type
328 * SQSL_ARRAY_TABLE_END.
329 * - bool: A single byte with value 1 representing true and 0 false.
330 * - null: No data.
331 */
332
334 enum SQSaveLoadType {
335 SQSL_INT = 0x00,
336 SQSL_STRING = 0x01,
337 SQSL_ARRAY = 0x02,
338 SQSL_TABLE = 0x03,
339 SQSL_BOOL = 0x04,
340 SQSL_NULL = 0x05,
341 SQSL_ARRAY_TABLE_END = 0xFF,
342 };
343
344 static byte _script_sl_byte;
345
347 static const SaveLoad _script_byte[] = {
348 SLEG_VAR("type", _script_sl_byte, SLE_UINT8),
349 };
350
351 /* static */ bool ScriptInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
352 {
353 if (max_depth == 0) {
354 ScriptLog::Error("Savedata can only be nested to 25 deep. No data saved."); // SQUIRREL_MAX_DEPTH = 25
355 return false;
356 }
357
358 switch (sq_gettype(vm, index)) {
359 case OT_INTEGER: {
360 if (!test) {
361 _script_sl_byte = SQSL_INT;
362 SlObject(nullptr, _script_byte);
363 }
364 SQInteger res;
365 sq_getinteger(vm, index, &res);
366 if (!test) {
367 int64 value = (int64)res;
368 SlCopy(&value, 1, SLE_INT64);
369 }
370 return true;
371 }
372
373 case OT_STRING: {
374 if (!test) {
375 _script_sl_byte = SQSL_STRING;
376 SlObject(nullptr, _script_byte);
377 }
378 const SQChar *buf;
379 sq_getstring(vm, index, &buf);
380 size_t len = strlen(buf) + 1;
381 if (len >= 255) {
382 ScriptLog::Error("Maximum string length is 254 chars. No data saved.");
383 return false;
384 }
385 if (!test) {
386 _script_sl_byte = (byte)len;
387 SlObject(nullptr, _script_byte);
388 SlCopy(const_cast<char *>(buf), len, SLE_CHAR);
389 }
390 return true;
391 }
392
393 case OT_ARRAY: {
394 if (!test) {
395 _script_sl_byte = SQSL_ARRAY;
396 SlObject(nullptr, _script_byte);
397 }
398 sq_pushnull(vm);
399 while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
400 /* Store the value */
401 bool res = SaveObject(vm, -1, max_depth - 1, test);
402 sq_pop(vm, 2);
403 if (!res) {
404 sq_pop(vm, 1);
405 return false;
406 }
407 }
408 sq_pop(vm, 1);
409 if (!test) {
410 _script_sl_byte = SQSL_ARRAY_TABLE_END;
411 SlObject(nullptr, _script_byte);
412 }
413 return true;
414 }
415
416 case OT_TABLE: {
417 if (!test) {
418 _script_sl_byte = SQSL_TABLE;
419 SlObject(nullptr, _script_byte);
420 }
421 sq_pushnull(vm);
422 while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
423 /* Store the key + value */
424 bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
425 sq_pop(vm, 2);
426 if (!res) {
427 sq_pop(vm, 1);
428 return false;
429 }
430 }
431 sq_pop(vm, 1);
432 if (!test) {
433 _script_sl_byte = SQSL_ARRAY_TABLE_END;
434 SlObject(nullptr, _script_byte);
435 }
436 return true;
437 }
438
439 case OT_BOOL: {
440 if (!test) {
441 _script_sl_byte = SQSL_BOOL;
442 SlObject(nullptr, _script_byte);
443 }
444 SQBool res;
445 sq_getbool(vm, index, &res);
446 if (!test) {
447 _script_sl_byte = res ? 1 : 0;
448 SlObject(nullptr, _script_byte);
449 }
450 return true;
451 }
452
453 case OT_NULL: {
454 if (!test) {
455 _script_sl_byte = SQSL_NULL;
456 SlObject(nullptr, _script_byte);
457 }
458 return true;
459 }
460
461 default:
462 ScriptLog::Error("You tried to save an unsupported type. No data saved.");
463 return false;
464 }
465 }
466
467 /* static */ void ScriptInstance::SaveEmpty()
468 {
469 _script_sl_byte = 0;
470 SlObject(nullptr, _script_byte);
471 }
472
473 void ScriptInstance::Save()
474 {
475 ScriptObject::ActiveInstance active(this);
476
477 /* Don't save data if the script didn't start yet or if it crashed. */
478 if (this->engine == nullptr || this->engine->HasScriptCrashed()) {
479 SaveEmpty();
480 return;
481 }
482
483 HSQUIRRELVM vm = this->engine->GetVM();
484 if (this->is_save_data_on_stack) {
485 _script_sl_byte = 1;
486 SlObject(nullptr, _script_byte);
487 /* Save the data that was just loaded. */
488 SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
489 } else if (!this->is_started) {
490 SaveEmpty();
491 return;
492 } else if (this->engine->MethodExists(*this->instance, "Save")) {
493 HSQOBJECT savedata;
494 /* We don't want to be interrupted during the save function. */
495 bool backup_allow = ScriptObject::GetAllowDoCommand();
496 ScriptObject::SetAllowDoCommand(false);
497 try {
498 if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
499 /* The script crashed in the Save function. We can't kill
500 * it here, but do so in the next script tick. */
501 SaveEmpty();
502 this->engine->CrashOccurred();
503 return;
504 }
505 } catch (Script_FatalError &e) {
506 /* If we don't mark the script as dead here cleaning up the squirrel
507 * stack could throw Script_FatalError again. */
508 this->is_dead = true;
509 this->engine->ThrowError(e.GetErrorMessage().c_str());
510 this->engine->ResumeError();
511 SaveEmpty();
512 /* We can't kill the script here, so mark it as crashed (not dead) and
513 * kill it in the next script tick. */
514 this->is_dead = false;
515 this->engine->CrashOccurred();
516 return;
517 }
518 ScriptObject::SetAllowDoCommand(backup_allow);
519
520 if (!sq_istable(savedata)) {
521 ScriptLog::Error(this->engine->IsSuspended() ? "This script took too long to Save." : "Save function should return a table.");
522 SaveEmpty();
523 this->engine->CrashOccurred();
524 return;
525 }
526 sq_pushobject(vm, savedata);
527 if (SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, true)) {
528 _script_sl_byte = 1;
529 SlObject(nullptr, _script_byte);
530 SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
531 this->is_save_data_on_stack = true;
532 } else {
533 SaveEmpty();
534 this->engine->CrashOccurred();
535 }
536 } else {
537 ScriptLog::Warning("Save function is not implemented");
538 _script_sl_byte = 0;
539 SlObject(nullptr, _script_byte);
540 }
541 }
542
543 void ScriptInstance::Pause()
544 {
545 /* Suspend script. */
546 HSQUIRRELVM vm = this->engine->GetVM();
547 Squirrel::DecreaseOps(vm, _settings_game.script.script_max_opcode_till_suspend);
548
549 this->is_paused = true;
550 }
551
552 void ScriptInstance::Unpause()
553 {
554 this->is_paused = false;
555 }
556
557 bool ScriptInstance::IsPaused()
558 {
559 return this->is_paused;
560 }
561
562 /* static */ bool ScriptInstance::LoadObjects(HSQUIRRELVM vm)
563 {
564 SlObject(nullptr, _script_byte);
565 switch (_script_sl_byte) {
566 case SQSL_INT: {
567 int64 value;
568 SlCopy(&value, 1, IsSavegameVersionBefore(SLV_SCRIPT_INT64) ? SLE_INT32 : SLE_INT64);
569 if (vm != nullptr) sq_pushinteger(vm, (SQInteger)value);
570 return true;
571 }
572
573 case SQSL_STRING: {
574 SlObject(nullptr, _script_byte);
575 static char buf[std::numeric_limits<decltype(_script_sl_byte)>::max()];
576 SlCopy(buf, _script_sl_byte, SLE_CHAR);
577 StrMakeValidInPlace(buf, buf + _script_sl_byte);
578 if (vm != nullptr) sq_pushstring(vm, buf, -1);
579 return true;
580 }
581
582 case SQSL_ARRAY: {
583 if (vm != nullptr) sq_newarray(vm, 0);
584 while (LoadObjects(vm)) {
585 if (vm != nullptr) sq_arrayappend(vm, -2);
586 /* The value is popped from the stack by squirrel. */
587 }
588 return true;
589 }
590
591 case SQSL_TABLE: {
592 if (vm != nullptr) sq_newtable(vm);
593 while (LoadObjects(vm)) {
594 LoadObjects(vm);
595 if (vm != nullptr) sq_rawset(vm, -3);
596 /* The key (-2) and value (-1) are popped from the stack by squirrel. */
597 }
598 return true;
599 }
600
601 case SQSL_BOOL: {
602 SlObject(nullptr, _script_byte);
603 if (vm != nullptr) sq_pushbool(vm, (SQBool)(_script_sl_byte != 0));
604 return true;
605 }
606
607 case SQSL_NULL: {
608 if (vm != nullptr) sq_pushnull(vm);
609 return true;
610 }
611
612 case SQSL_ARRAY_TABLE_END: {
613 return false;
614 }
615
616 default: NOT_REACHED();
617 }
618 }
619
620 /* static */ void ScriptInstance::LoadEmpty()
621 {
622 SlObject(nullptr, _script_byte);
623 /* Check if there was anything saved at all. */
624 if (_script_sl_byte == 0) return;
625
626 LoadObjects(nullptr);
627 }
628
629 void ScriptInstance::Load(int version)
630 {
631 ScriptObject::ActiveInstance active(this);
632
633 if (this->engine == nullptr || version == -1) {
634 LoadEmpty();
635 return;
636 }
637 HSQUIRRELVM vm = this->engine->GetVM();
638
639 SlObject(nullptr, _script_byte);
640 /* Check if there was anything saved at all. */
641 if (_script_sl_byte == 0) return;
642
643 sq_pushinteger(vm, version);
644 LoadObjects(vm);
645 this->is_save_data_on_stack = true;
646 }
647
648 bool ScriptInstance::CallLoad()
649 {
650 HSQUIRRELVM vm = this->engine->GetVM();
651 /* Is there save data that we should load? */
652 if (!this->is_save_data_on_stack) return true;
653 /* Whatever happens, after CallLoad the savegame data is removed from the stack. */
654 this->is_save_data_on_stack = false;
655
656 if (!this->engine->MethodExists(*this->instance, "Load")) {
657 ScriptLog::Warning("Loading failed: there was data for the script to load, but the script does not have a Load() function.");
658
659 /* Pop the savegame data and version. */
660 sq_pop(vm, 2);
661 return true;
662 }
663
664 /* Go to the instance-root */
665 sq_pushobject(vm, *this->instance);
666 /* Find the function-name inside the script */
667 sq_pushstring(vm, "Load", -1);
668 /* Change the "Load" string in a function pointer */
669 sq_get(vm, -2);
670 /* Push the main instance as "this" object */
671 sq_pushobject(vm, *this->instance);
672 /* Push the version data and savegame data as arguments */
673 sq_push(vm, -5);
674 sq_push(vm, -5);
675
676 /* Call the script load function. sq_call removes the arguments (but not the
677 * function pointer) from the stack. */
678 if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
679
680 /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
681 sq_pop(vm, 4);
682 return true;
683 }
684
685 SQInteger ScriptInstance::GetOpsTillSuspend()
686 {
687 return this->engine->GetOpsTillSuspend();
688 }
689
690 bool ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
691 {
692 ScriptObject::ActiveInstance active(this);
693
694 if (!ScriptObject::CheckLastCommand(tile, p1, p2, cmd)) {
695 Debug(script, 1, "DoCommandCallback terminating a script, last command does not match expected command");
696 return false;
697 }
698
699 ScriptObject::SetLastCommandRes(result.Succeeded());
700
701 if (result.Failed()) {
702 ScriptObject::SetLastError(ScriptError::StringToError(result.GetErrorMessage()));
703 } else {
704 ScriptObject::IncreaseDoCommandCosts(result.GetCost());
705 ScriptObject::SetLastCost(result.GetCost());
706 }
707
708 ScriptObject::SetLastCommand(INVALID_TILE, 0, 0, CMD_END);
709
710 return true;
711 }
712
713 void ScriptInstance::InsertEvent(class ScriptEvent *event)
714 {
715 ScriptObject::ActiveInstance active(this);
716
717 ScriptEventController::InsertEvent(event);
718 }
719
720 size_t ScriptInstance::GetAllocatedMemory() const
721 {
722 if (this->engine == nullptr) return this->last_allocated_memory;
723 return this->engine->GetAllocatedMemory();
724 }
725
726 void ScriptInstance::ReleaseSQObject(HSQOBJECT *obj)
727 {
728 if (!this->in_shutdown) this->engine->ReleaseObject(obj);
729 }
ScriptInstance::LoadDummyScript
virtual void LoadDummyScript()=0
Load the dummy script.
Script_Suspend
A throw-class that is given when the script wants to suspend.
Definition: script_suspend.hpp:21
ScriptStorage::log_data
void * log_data
Pointer to the log data storage.
Definition: script_storage.hpp:65
TileIndex
uint32 TileIndex
The index/ID of a Tile.
Definition: tile_type.h:83
ScriptInstance::SaveObject
static bool SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
Save one object (int / string / array / table) to the savegame.
Definition: script_instance.cpp:351
ScriptInstance::engine
class Squirrel * engine
A wrapper around the squirrel vm.
Definition: script_instance.hpp:215
_script_sl_byte
static byte _script_sl_byte
Used as source/target by the script saveload code to store/load a single byte.
Definition: script_instance.cpp:344
PrintFunc
static void PrintFunc(bool error_msg, const SQChar *message)
Callback called by squirrel when a script uses "print" and for error messages.
Definition: script_instance.cpp:44
ScriptInstance::last_allocated_memory
size_t last_allocated_memory
Last known allocated memory value (for display for crashed scripts)
Definition: script_instance.hpp:258
ScriptStorage
The storage for each script.
Definition: script_storage.hpp:31
ScriptInstance::CallLoad
bool CallLoad()
Call the script Load function if it exists and data was loaded from a savegame.
Definition: script_instance.cpp:648
ScriptInstance::is_dead
bool is_dead
True if the script has been stopped.
Definition: script_instance.hpp:252
ScriptInstance::InsertEvent
void InsertEvent(class ScriptEvent *event)
Insert an event for this script.
Definition: script_instance.cpp:713
ScriptInstance::GameLoop
void GameLoop()
Run the GameLoop of a script.
Definition: script_instance.cpp:168
Squirrel::SetPrintFunction
void SetPrintFunction(SQPrintFunc *func)
Set a custom print function, so you can handle outputs from SQ yourself.
Definition: squirrel.hpp:231
Script_Suspend::GetSuspendTime
int GetSuspendTime()
Get the amount of ticks the script should be suspended.
Definition: script_suspend.hpp:37
ScriptInstance::DoCommandCallback
bool DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
DoCommand callback function for all commands executed by scripts.
Definition: script_instance.cpp:690
ScriptInstance::controller
class ScriptController * controller
The script main class.
Definition: script_instance.hpp:247
ScriptInstance::Save
void Save()
Call the script Save function and save all data in the savegame.
Definition: script_instance.cpp:473
ScriptScanner::engine
class Squirrel * engine
The engine we're scanning with.
Definition: script_scanner.hpp:86
SQSL_STRING
@ SQSL_STRING
The following data is an string.
Definition: script_instance.cpp:336
SQSL_ARRAY
@ SQSL_ARRAY
The following data is an array.
Definition: script_instance.cpp:337
SQSL_INT
@ SQSL_INT
The following data is an integer.
Definition: script_instance.cpp:335
SlCopy
void SlCopy(void *object, size_t length, VarType conv)
Copy a list of SL_VARs to/from a savegame.
Definition: saveload.cpp:1151
ScriptInstance
Runtime information about a script like a pointer to the squirrel vm and the current state.
Definition: script_instance.hpp:23
ScriptInstance::ScriptInstance
ScriptInstance(const char *APIName)
Create a new script.
Definition: script_instance.cpp:50
ScriptInstance::Unpause
void Unpause()
Resume execution of the script.
Definition: script_instance.cpp:552
ScriptInstance::DoCommandReturnStoryPageElementID
static void DoCommandReturnStoryPageElementID(ScriptInstance *instance)
Return a StoryPageElementID reply for a DoCommand.
Definition: script_instance.cpp:295
ScriptInstance::LoadObjects
static bool LoadObjects(HSQUIRRELVM vm)
Load all objects from a savegame.
Definition: script_instance.cpp:562
Searchpath
Searchpath
Types of searchpaths OpenTTD might use.
Definition: fileio_type.h:131
ScriptInstance::instance
SQObject * instance
Squirrel-pointer to the script main class.
Definition: script_instance.hpp:249
ScriptInstance::is_save_data_on_stack
bool is_save_data_on_stack
Is the save data still on the squirrel stack?
Definition: script_instance.hpp:253
Squirrel::GetVM
HSQUIRRELVM GetVM()
Get the squirrel VM.
Definition: squirrel.hpp:80
ScriptInstance::is_paused
bool is_paused
Is the script paused? (a paused script will not be executed until unpaused)
Definition: script_instance.hpp:255
SQSaveLoadType
SQSaveLoadType
The type of the data that follows in the savegame.
Definition: script_instance.cpp:334
Script_FatalError::GetErrorMessage
const std::string & GetErrorMessage() const
The error message associated with the fatal error.
Definition: script_fatalerror.hpp:30
Owner
Owner
Enum for all companies/owners.
Definition: company_type.h:18
CommandCost::GetErrorMessage
StringID GetErrorMessage() const
Returns the error message of a command.
Definition: command_type.h:140
SQSL_TABLE
@ SQSL_TABLE
The following data is an table.
Definition: script_instance.cpp:338
CommandCost::Succeeded
bool Succeeded() const
Did this command succeed?
Definition: command_type.h:150
ScriptInstance::GetOpsTillSuspend
SQInteger GetOpsTillSuspend()
Get the number of operations the script can execute before being suspended.
Definition: script_instance.cpp:685
SQSL_BOOL
@ SQSL_BOOL
The following data is a boolean.
Definition: script_instance.cpp:339
ScriptInstance::Pause
void Pause()
Suspends the script for the current tick and then pause the execution of script.
Definition: script_instance.cpp:543
ScriptInstance::DoCommandReturnGoalID
static void DoCommandReturnGoalID(ScriptInstance *instance)
Return a GoalID reply for a DoCommand.
Definition: script_instance.cpp:285
ScriptInstance::ReleaseSQObject
void ReleaseSQObject(HSQOBJECT *obj)
Decrease the ref count of a squirrel object.
Definition: script_instance.cpp:726
ScriptInstance::storage
class ScriptStorage * storage
Some global information for each running script.
Definition: script_instance.hpp:248
Squirrel::GetOpsTillSuspend
SQInteger GetOpsTillSuspend()
How many operations can we execute till suspension?
Definition: squirrel.cpp:816
Squirrel
Definition: squirrel.hpp:23
Squirrel::HasScriptCrashed
bool HasScriptCrashed()
Find out if the squirrel script made an error before.
Definition: squirrel.cpp:800
Squirrel::LoadScript
bool LoadScript(const char *script)
Load a script.
Definition: squirrel.cpp:743
ScriptInstance::DoCommandReturnSignID
static void DoCommandReturnSignID(ScriptInstance *instance)
Return a SignID reply for a DoCommand.
Definition: script_instance.cpp:275
CommandCost
Common return value for all commands.
Definition: command_type.h:23
ScriptInstance::in_shutdown
bool in_shutdown
Is this instance currently being destructed?
Definition: script_instance.hpp:256
script_storage.hpp
_script_byte
static const SaveLoad _script_byte[]
SaveLoad array that saves/loads exactly one byte.
Definition: script_instance.cpp:347
Squirrel::ThrowError
void ThrowError(const char *error)
Throw a Squirrel error that will be nicely displayed to the user.
Definition: squirrel.hpp:236
GameSettings::script
ScriptSettings script
settings for scripts
Definition: settings_type.h:579
StrMakeValidInPlace
void StrMakeValidInPlace(char *str, const char *last, StringValidationSettings settings)
Scans the string for invalid characters and replaces then with a question mark '?' (if not ignored).
Definition: string.cpp:255
ScriptInstance::DoCommandReturn
static void DoCommandReturn(ScriptInstance *instance)
Return a true/false reply for a DoCommand.
Definition: script_instance.cpp:265
ScriptInstance::SaveEmpty
static void SaveEmpty()
Don't save any data in the savegame.
Definition: script_instance.cpp:467
SQSL_NULL
@ SQSL_NULL
A null variable.
Definition: script_instance.cpp:340
FileExists
bool FileExists(const std::string &filename)
Test whether the given filename exists.
Definition: fileio.cpp:122
SLEG_VAR
#define SLEG_VAR(name, variable, type)
Storage of a global variable in every savegame version.
Definition: saveload.h:936
Squirrel::SetGlobalPointer
void SetGlobalPointer(void *ptr)
Sets a pointer in the VM that is reachable from where ever you are in SQ.
Definition: squirrel.hpp:221
CommandCost::Failed
bool Failed() const
Did this command fail?
Definition: command_type.h:159
ScriptInstance::IsDead
bool IsDead() const
Return the "this script died" value.
Definition: script_instance.hpp:126
ScriptStorage::event_data
void * event_data
Pointer to the event data storage.
Definition: script_storage.hpp:64
Squirrel::GetAllocatedMemory
size_t GetAllocatedMemory() const noexcept
Get number of bytes allocated by this VM.
Definition: squirrel.cpp:192
_settings_game
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:53
CommandCost::GetCost
Money GetCost() const
The costs as made up to this moment.
Definition: command_type.h:82
ScriptInstance::is_started
bool is_started
Is the scripts constructor executed?
Definition: script_instance.hpp:251
IsSavegameVersionBefore
static bool IsSavegameVersionBefore(SaveLoadVersion major, byte minor=0)
Checks whether the savegame is below major.
Definition: saveload.h:1023
ScriptInstance::DoCommandReturnStoryPageID
static void DoCommandReturnStoryPageID(ScriptInstance *instance)
Return a StoryPageID reply for a DoCommand.
Definition: script_instance.cpp:290
ScriptInstance::RegisterAPI
virtual void RegisterAPI()
Register all API functions to the VM.
Definition: script_instance.cpp:109
Squirrel::CallMethod
bool CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend)
Call a method of an instance, in various flavors.
Definition: squirrel.cpp:411
ScriptInstance::Initialize
void Initialize(const char *main_script, const char *instance_name, CompanyID company)
Initialize the script and prepare it for its first run.
Definition: script_instance.cpp:69
ScriptScanner::main_script
std::string main_script
The full path of the script.
Definition: script_scanner.hpp:87
ScriptInstance::LoadEmpty
static void LoadEmpty()
Load and discard data from a savegame.
Definition: script_instance.cpp:620
ScriptInstance::DoCommandReturnGroupID
static void DoCommandReturnGroupID(ScriptInstance *instance)
Return a GroupID reply for a DoCommand.
Definition: script_instance.cpp:280
Script_Suspend::GetSuspendCallback
Script_SuspendCallbackProc * GetSuspendCallback()
Get the callback to call when the script can run again.
Definition: script_suspend.hpp:43
squirrel_register_std
void squirrel_register_std(Squirrel *engine)
Register all standard functions we want to give to a script.
Definition: squirrel_std.cpp:102
SLV_SCRIPT_INT64
@ SLV_SCRIPT_INT64
296 PR#9415 SQInteger is 64bit but was saved as 32bit.
Definition: saveload.h:339
Squirrel::ResumeError
void ResumeError()
Resume the VM with an error so it prints a stack trace.
Definition: squirrel.cpp:398
Squirrel::CreateClassInstance
bool CreateClassInstance(const char *class_name, void *real_instance, HSQOBJECT *instance)
Exactly the same as CreateClassInstanceVM, only callable without instance of Squirrel.
Definition: squirrel.cpp:523
_current_company
CompanyID _current_company
Company currently doing an action.
Definition: company_cmd.cpp:47
MAX_CONSTRUCTOR_OPS
static const int MAX_CONSTRUCTOR_OPS
The maximum number of operations for initial start of a script.
Definition: script_info.hpp:21
Squirrel::CollectGarbage
void CollectGarbage()
Tell the VM to do a garbage collection run.
Definition: squirrel.cpp:405
ScriptInstance::callback
Script_SuspendCallbackProc * callback
Callback that should be called in the next tick the script runs.
Definition: script_instance.hpp:257
Squirrel::MethodExists
bool MethodExists(HSQOBJECT instance, const char *method_name)
Check if a method exists in an instance.
Definition: squirrel.cpp:358
Squirrel::CrashOccurred
void CrashOccurred()
Set the script status to crashed.
Definition: squirrel.cpp:805
ScriptSettings::script_max_opcode_till_suspend
uint32 script_max_opcode_till_suspend
max opcode calls till scripts will suspend
Definition: settings_type.h:369
script_info.hpp
ScriptInstance::DoCommandReturnVehicleID
static void DoCommandReturnVehicleID(ScriptInstance *instance)
Return a VehicleID reply for a DoCommand.
Definition: script_instance.cpp:270
ScriptInstance::GetStorage
class ScriptStorage * GetStorage()
Get the storage of this script.
Definition: script_instance.cpp:300
seprintf
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:535
script_instance.hpp
Subdirectory
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition: fileio_type.h:108
ScriptInstance::Died
virtual void Died()
Tell the script it died.
Definition: script_instance.cpp:153
ScriptInstance::suspend
int suspend
The amount of ticks to suspend this script before it's allowed to continue.
Definition: script_instance.hpp:254
Squirrel::Resume
bool Resume(int suspend=-1)
Resume a VM when it was suspended via a throw.
Definition: squirrel.cpp:376
Script_FatalError
A throw-class that is given when the script made a fatal error.
Definition: script_fatalerror.hpp:16
ScriptInstance::Continue
void Continue()
A script in multiplayer waits for the server to handle its DoCommand.
Definition: script_instance.cpp:147
Debug
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
script_fatalerror.hpp
Squirrel::ReleaseObject
void ReleaseObject(HSQOBJECT *ptr)
Release a SQ object.
Definition: squirrel.hpp:241
INVALID_TILE
static const TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition: tile_type.h:88
ScriptInstance::IsPaused
bool IsPaused()
Checks if the script is paused.
Definition: script_instance.cpp:557
Squirrel::IsSuspended
bool IsSuspended()
Did the squirrel code suspend or return normally.
Definition: squirrel.cpp:795
ScriptInstance::LoadCompatibilityScripts
bool LoadCompatibilityScripts(const char *api_version, Subdirectory dir)
Load squirrel scripts to emulate an older API.
Definition: script_instance.cpp:115
ScriptInstance::CollectGarbage
void CollectGarbage()
Let the VM collect any garbage.
Definition: script_instance.cpp:257
CMD_END
@ CMD_END
Must ALWAYS be on the end of this list!! (period)
Definition: command_type.h:338
ScriptInstance::Load
void Load(int version)
Load data from a savegame and store it on the stack.
Definition: script_instance.cpp:629
SlObject
void SlObject(void *object, const SaveLoadTable &slt)
Main SaveLoad function.
Definition: saveload.cpp:1838
SQSL_ARRAY_TABLE_END
@ SQSL_ARRAY_TABLE_END
Marks the end of an array or table, no data follows.
Definition: script_instance.cpp:341
SaveLoad
SaveLoad type struct.
Definition: saveload.h:652
Squirrel::DecreaseOps
static void DecreaseOps(HSQUIRRELVM vm, int amount)
Tell the VM to remove amount ops from the number of ops till suspend.
Definition: squirrel.cpp:790
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:394
ScriptInstance::GetLogPointer
void * GetLogPointer()
Get the log pointer of this script.
Definition: script_instance.cpp:305
SQUIRREL_MAX_DEPTH
static const uint SQUIRREL_MAX_DEPTH
The maximum recursive depth for items stored in the savegame.
Definition: script_instance.hpp:20
MAX_SL_OPS
static const int MAX_SL_OPS
The maximum number of operations for saving or loading the data of a script.
Definition: script_info.hpp:19