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