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