OpenTTD
script_instance.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "../stdafx.h"
13 #include "../debug.h"
14 #include "../saveload/saveload.h"
15 
16 #include "../script/squirrel_class.hpp"
17 
18 #include "script_fatalerror.hpp"
19 #include "script_storage.hpp"
20 #include "script_info.hpp"
21 #include "script_instance.hpp"
22 
23 #include "api/script_controller.hpp"
24 #include "api/script_error.hpp"
25 #include "api/script_event.hpp"
26 #include "api/script_log.hpp"
27 
28 #include "../company_base.h"
29 #include "../company_func.h"
30 #include "../fileio_func.h"
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  callback(nullptr)
64 {
65  this->storage = new ScriptStorage();
66  this->engine = new Squirrel(APIName);
68 }
69 
70 void ScriptInstance::Initialize(const char *main_script, const char *instance_name, CompanyID company)
71 {
72  ScriptObject::ActiveInstance active(this);
73 
74  this->controller = new ScriptController(company);
75 
76  /* Register the API functions and classes */
77  this->engine->SetGlobalPointer(this->engine);
78  this->RegisterAPI();
79 
80  try {
81  ScriptObject::SetAllowDoCommand(false);
82  /* Load and execute the script for this script */
83  if (strcmp(main_script, "%_dummy") == 0) {
84  this->LoadDummyScript();
85  } else if (!this->engine->LoadScript(main_script) || this->engine->IsSuspended()) {
86  if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to load script. AI is not started.");
87  this->Died();
88  return;
89  }
90 
91  /* Create the main-class */
92  this->instance = MallocT<SQObject>(1);
93  if (!this->engine->CreateClassInstance(instance_name, this->controller, this->instance)) {
94  this->Died();
95  return;
96  }
97  ScriptObject::SetAllowDoCommand(true);
98  } catch (Script_FatalError &e) {
99  this->is_dead = true;
100  this->engine->ThrowError(e.GetErrorMessage());
101  this->engine->ResumeError();
102  this->Died();
103  }
104 }
105 
107 {
108  extern void squirrel_register_std(Squirrel *engine);
109  squirrel_register_std(this->engine);
110 }
111 
112 bool ScriptInstance::LoadCompatibilityScripts(const char *api_version, Subdirectory dir)
113 {
114  char script_name[32];
115  seprintf(script_name, lastof(script_name), "compat_%s.nut", api_version);
116  char buf[MAX_PATH];
117  Searchpath sp;
118  FOR_ALL_SEARCHPATHS(sp) {
119  FioAppendDirectory(buf, lastof(buf), sp, dir);
120  strecat(buf, script_name, lastof(buf));
121  if (!FileExists(buf)) continue;
122 
123  if (this->engine->LoadScript(buf)) return true;
124 
125  ScriptLog::Error("Failed to load API compatibility script");
126  DEBUG(script, 0, "Error compiling / running API compatibility script: %s", buf);
127  return false;
128  }
129 
130  ScriptLog::Warning("API compatibility script not found");
131  return true;
132 }
133 
134 ScriptInstance::~ScriptInstance()
135 {
136  ScriptObject::ActiveInstance active(this);
137 
138  if (instance != nullptr) this->engine->ReleaseObject(this->instance);
139  if (engine != nullptr) delete this->engine;
140  delete this->storage;
141  delete this->controller;
142  free(this->instance);
143 }
144 
146 {
147  assert(this->suspend < 0);
148  this->suspend = -this->suspend - 1;
149 }
150 
152 {
153  DEBUG(script, 0, "The script died unexpectedly.");
154  this->is_dead = true;
155 
156  this->last_allocated_memory = this->GetAllocatedMemory(); // Update cache
157 
158  if (this->instance != nullptr) this->engine->ReleaseObject(this->instance);
159  delete this->engine;
160  this->instance = nullptr;
161  this->engine = nullptr;
162 }
163 
165 {
166  ScriptObject::ActiveInstance active(this);
167 
168  if (this->IsDead()) return;
169  if (this->engine->HasScriptCrashed()) {
170  /* The script crashed during saving, kill it here. */
171  this->Died();
172  return;
173  }
174  if (this->is_paused) return;
175  this->controller->ticks++;
176 
177  if (this->suspend < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
178  if (this->suspend < 0) return; // Multiplayer suspend, wait for Continue().
179  if (--this->suspend > 0) return; // Singleplayer suspend, decrease to 0.
180 
181  _current_company = ScriptObject::GetCompany();
182 
183  /* If there is a callback to call, call that first */
184  if (this->callback != nullptr) {
185  if (this->is_save_data_on_stack) {
186  sq_poptop(this->engine->GetVM());
187  this->is_save_data_on_stack = false;
188  }
189  try {
190  this->callback(this);
191  } catch (Script_Suspend &e) {
192  this->suspend = e.GetSuspendTime();
193  this->callback = e.GetSuspendCallback();
194 
195  return;
196  }
197  }
198 
199  this->suspend = 0;
200  this->callback = nullptr;
201 
202  if (!this->is_started) {
203  try {
204  ScriptObject::SetAllowDoCommand(false);
205  /* Run the constructor if it exists. Don't allow any DoCommands in it. */
206  if (this->engine->MethodExists(*this->instance, "constructor")) {
207  if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
208  if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to initialize. Script is not started.");
209  this->Died();
210  return;
211  }
212  }
213  if (!this->CallLoad() || this->engine->IsSuspended()) {
214  if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long in the Load function. Script is not started.");
215  this->Died();
216  return;
217  }
218  ScriptObject::SetAllowDoCommand(true);
219  /* Start the script by calling Start() */
220  if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.script.script_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
221  } catch (Script_Suspend &e) {
222  this->suspend = e.GetSuspendTime();
223  this->callback = e.GetSuspendCallback();
224  } catch (Script_FatalError &e) {
225  this->is_dead = true;
226  this->engine->ThrowError(e.GetErrorMessage());
227  this->engine->ResumeError();
228  this->Died();
229  }
230 
231  this->is_started = true;
232  return;
233  }
234  if (this->is_save_data_on_stack) {
235  sq_poptop(this->engine->GetVM());
236  this->is_save_data_on_stack = false;
237  }
238 
239  /* Continue the VM */
240  try {
242  } catch (Script_Suspend &e) {
243  this->suspend = e.GetSuspendTime();
244  this->callback = e.GetSuspendCallback();
245  } catch (Script_FatalError &e) {
246  this->is_dead = true;
247  this->engine->ThrowError(e.GetErrorMessage());
248  this->engine->ResumeError();
249  this->Died();
250  }
251 }
252 
254 {
255  if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
256 }
257 
259 {
260  instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
261 }
262 
264 {
265  instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
266 }
267 
269 {
270  instance->engine->InsertResult(ScriptObject::GetNewSignID());
271 }
272 
274 {
275  instance->engine->InsertResult(ScriptObject::GetNewGroupID());
276 }
277 
279 {
280  instance->engine->InsertResult(ScriptObject::GetNewGoalID());
281 }
282 
284 {
285  instance->engine->InsertResult(ScriptObject::GetNewStoryPageID());
286 }
287 
289 {
290  instance->engine->InsertResult(ScriptObject::GetNewStoryPageElementID());
291 }
292 
294 {
295  return this->storage;
296 }
297 
299 {
300  ScriptObject::ActiveInstance active(this);
301 
302  return ScriptObject::GetLogPointer();
303 }
304 
305 /*
306  * All data is stored in the following format:
307  * First 1 byte indicating if there is a data blob at all.
308  * 1 byte indicating the type of data.
309  * The data itself, this differs per type:
310  * - integer: a binary representation of the integer (int32).
311  * - string: First one byte with the string length, then a 0-terminated char
312  * array. The string can't be longer than 255 bytes (including
313  * terminating '\0').
314  * - array: All data-elements of the array are saved recursive in this
315  * format, and ended with an element of the type
316  * SQSL_ARRAY_TABLE_END.
317  * - table: All key/value pairs are saved in this format (first key 1, then
318  * value 1, then key 2, etc.). All keys and values can have an
319  * arbitrary type (as long as it is supported by the save function
320  * of course). The table is ended with an element of the type
321  * SQSL_ARRAY_TABLE_END.
322  * - bool: A single byte with value 1 representing true and 0 false.
323  * - null: No data.
324  */
325 
328  SQSL_INT = 0x00,
329  SQSL_STRING = 0x01,
330  SQSL_ARRAY = 0x02,
331  SQSL_TABLE = 0x03,
332  SQSL_BOOL = 0x04,
333  SQSL_NULL = 0x05,
335 };
336 
337 static byte _script_sl_byte;
338 
340 static const SaveLoad _script_byte[] = {
341  SLEG_VAR(_script_sl_byte, SLE_UINT8),
342  SLE_END()
343 };
344 
345 /* static */ bool ScriptInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
346 {
347  if (max_depth == 0) {
348  ScriptLog::Error("Savedata can only be nested to 25 deep. No data saved."); // SQUIRREL_MAX_DEPTH = 25
349  return false;
350  }
351 
352  switch (sq_gettype(vm, index)) {
353  case OT_INTEGER: {
354  if (!test) {
355  _script_sl_byte = SQSL_INT;
356  SlObject(nullptr, _script_byte);
357  }
358  SQInteger res;
359  sq_getinteger(vm, index, &res);
360  if (!test) {
361  int value = (int)res;
362  SlArray(&value, 1, SLE_INT32);
363  }
364  return true;
365  }
366 
367  case OT_STRING: {
368  if (!test) {
369  _script_sl_byte = SQSL_STRING;
370  SlObject(nullptr, _script_byte);
371  }
372  const SQChar *buf;
373  sq_getstring(vm, index, &buf);
374  size_t len = strlen(buf) + 1;
375  if (len >= 255) {
376  ScriptLog::Error("Maximum string length is 254 chars. No data saved.");
377  return false;
378  }
379  if (!test) {
380  _script_sl_byte = (byte)len;
381  SlObject(nullptr, _script_byte);
382  SlArray(const_cast<char *>(buf), len, SLE_CHAR);
383  }
384  return true;
385  }
386 
387  case OT_ARRAY: {
388  if (!test) {
389  _script_sl_byte = SQSL_ARRAY;
390  SlObject(nullptr, _script_byte);
391  }
392  sq_pushnull(vm);
393  while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
394  /* Store the value */
395  bool res = SaveObject(vm, -1, max_depth - 1, test);
396  sq_pop(vm, 2);
397  if (!res) {
398  sq_pop(vm, 1);
399  return false;
400  }
401  }
402  sq_pop(vm, 1);
403  if (!test) {
404  _script_sl_byte = SQSL_ARRAY_TABLE_END;
405  SlObject(nullptr, _script_byte);
406  }
407  return true;
408  }
409 
410  case OT_TABLE: {
411  if (!test) {
412  _script_sl_byte = SQSL_TABLE;
413  SlObject(nullptr, _script_byte);
414  }
415  sq_pushnull(vm);
416  while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
417  /* Store the key + value */
418  bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
419  sq_pop(vm, 2);
420  if (!res) {
421  sq_pop(vm, 1);
422  return false;
423  }
424  }
425  sq_pop(vm, 1);
426  if (!test) {
427  _script_sl_byte = SQSL_ARRAY_TABLE_END;
428  SlObject(nullptr, _script_byte);
429  }
430  return true;
431  }
432 
433  case OT_BOOL: {
434  if (!test) {
435  _script_sl_byte = SQSL_BOOL;
436  SlObject(nullptr, _script_byte);
437  }
438  SQBool res;
439  sq_getbool(vm, index, &res);
440  if (!test) {
441  _script_sl_byte = res ? 1 : 0;
442  SlObject(nullptr, _script_byte);
443  }
444  return true;
445  }
446 
447  case OT_NULL: {
448  if (!test) {
449  _script_sl_byte = SQSL_NULL;
450  SlObject(nullptr, _script_byte);
451  }
452  return true;
453  }
454 
455  default:
456  ScriptLog::Error("You tried to save an unsupported type. No data saved.");
457  return false;
458  }
459 }
460 
461 /* static */ void ScriptInstance::SaveEmpty()
462 {
463  _script_sl_byte = 0;
464  SlObject(nullptr, _script_byte);
465 }
466 
468 {
469  ScriptObject::ActiveInstance active(this);
470 
471  /* Don't save data if the script didn't start yet or if it crashed. */
472  if (this->engine == nullptr || this->engine->HasScriptCrashed()) {
473  SaveEmpty();
474  return;
475  }
476 
477  HSQUIRRELVM vm = this->engine->GetVM();
478  if (this->is_save_data_on_stack) {
479  _script_sl_byte = 1;
480  SlObject(nullptr, _script_byte);
481  /* Save the data that was just loaded. */
482  SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
483  } else if (!this->is_started) {
484  SaveEmpty();
485  return;
486  } else if (this->engine->MethodExists(*this->instance, "Save")) {
487  HSQOBJECT savedata;
488  /* We don't want to be interrupted during the save function. */
489  bool backup_allow = ScriptObject::GetAllowDoCommand();
490  ScriptObject::SetAllowDoCommand(false);
491  try {
492  if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
493  /* The script crashed in the Save function. We can't kill
494  * it here, but do so in the next script tick. */
495  SaveEmpty();
496  this->engine->CrashOccurred();
497  return;
498  }
499  } catch (Script_FatalError &e) {
500  /* If we don't mark the script as dead here cleaning up the squirrel
501  * stack could throw Script_FatalError again. */
502  this->is_dead = true;
503  this->engine->ThrowError(e.GetErrorMessage());
504  this->engine->ResumeError();
505  SaveEmpty();
506  /* We can't kill the script here, so mark it as crashed (not dead) and
507  * kill it in the next script tick. */
508  this->is_dead = false;
509  this->engine->CrashOccurred();
510  return;
511  }
512  ScriptObject::SetAllowDoCommand(backup_allow);
513 
514  if (!sq_istable(savedata)) {
515  ScriptLog::Error(this->engine->IsSuspended() ? "This script took too long to Save." : "Save function should return a table.");
516  SaveEmpty();
517  this->engine->CrashOccurred();
518  return;
519  }
520  sq_pushobject(vm, savedata);
521  if (SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, true)) {
522  _script_sl_byte = 1;
523  SlObject(nullptr, _script_byte);
524  SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
525  this->is_save_data_on_stack = true;
526  } else {
527  SaveEmpty();
528  this->engine->CrashOccurred();
529  }
530  } else {
531  ScriptLog::Warning("Save function is not implemented");
532  _script_sl_byte = 0;
533  SlObject(nullptr, _script_byte);
534  }
535 }
536 
538 {
539  /* Suspend script. */
540  HSQUIRRELVM vm = this->engine->GetVM();
542 
543  this->is_paused = true;
544 }
545 
547 {
548  this->is_paused = false;
549 }
550 
552 {
553  return this->is_paused;
554 }
555 
556 /* static */ bool ScriptInstance::LoadObjects(HSQUIRRELVM vm)
557 {
558  SlObject(nullptr, _script_byte);
559  switch (_script_sl_byte) {
560  case SQSL_INT: {
561  int value;
562  SlArray(&value, 1, SLE_INT32);
563  if (vm != nullptr) sq_pushinteger(vm, (SQInteger)value);
564  return true;
565  }
566 
567  case SQSL_STRING: {
568  SlObject(nullptr, _script_byte);
569  static char buf[256];
570  SlArray(buf, _script_sl_byte, SLE_CHAR);
571  if (vm != nullptr) sq_pushstring(vm, buf, -1);
572  return true;
573  }
574 
575  case SQSL_ARRAY: {
576  if (vm != nullptr) sq_newarray(vm, 0);
577  while (LoadObjects(vm)) {
578  if (vm != nullptr) sq_arrayappend(vm, -2);
579  /* The value is popped from the stack by squirrel. */
580  }
581  return true;
582  }
583 
584  case SQSL_TABLE: {
585  if (vm != nullptr) sq_newtable(vm);
586  while (LoadObjects(vm)) {
587  LoadObjects(vm);
588  if (vm != nullptr) sq_rawset(vm, -3);
589  /* The key (-2) and value (-1) are popped from the stack by squirrel. */
590  }
591  return true;
592  }
593 
594  case SQSL_BOOL: {
595  SlObject(nullptr, _script_byte);
596  if (vm != nullptr) sq_pushbool(vm, (SQBool)(_script_sl_byte != 0));
597  return true;
598  }
599 
600  case SQSL_NULL: {
601  if (vm != nullptr) sq_pushnull(vm);
602  return true;
603  }
604 
605  case SQSL_ARRAY_TABLE_END: {
606  return false;
607  }
608 
609  default: NOT_REACHED();
610  }
611 }
612 
613 /* static */ void ScriptInstance::LoadEmpty()
614 {
615  SlObject(nullptr, _script_byte);
616  /* Check if there was anything saved at all. */
617  if (_script_sl_byte == 0) return;
618 
619  LoadObjects(nullptr);
620 }
621 
622 void ScriptInstance::Load(int version)
623 {
624  ScriptObject::ActiveInstance active(this);
625 
626  if (this->engine == nullptr || version == -1) {
627  LoadEmpty();
628  return;
629  }
630  HSQUIRRELVM vm = this->engine->GetVM();
631 
632  SlObject(nullptr, _script_byte);
633  /* Check if there was anything saved at all. */
634  if (_script_sl_byte == 0) return;
635 
636  sq_pushinteger(vm, version);
637  LoadObjects(vm);
638  this->is_save_data_on_stack = true;
639 }
640 
642 {
643  HSQUIRRELVM vm = this->engine->GetVM();
644  /* Is there save data that we should load? */
645  if (!this->is_save_data_on_stack) return true;
646  /* Whatever happens, after CallLoad the savegame data is removed from the stack. */
647  this->is_save_data_on_stack = false;
648 
649  if (!this->engine->MethodExists(*this->instance, "Load")) {
650  ScriptLog::Warning("Loading failed: there was data for the script to load, but the script does not have a Load() function.");
651 
652  /* Pop the savegame data and version. */
653  sq_pop(vm, 2);
654  return true;
655  }
656 
657  /* Go to the instance-root */
658  sq_pushobject(vm, *this->instance);
659  /* Find the function-name inside the script */
660  sq_pushstring(vm, "Load", -1);
661  /* Change the "Load" string in a function pointer */
662  sq_get(vm, -2);
663  /* Push the main instance as "this" object */
664  sq_pushobject(vm, *this->instance);
665  /* Push the version data and savegame data as arguments */
666  sq_push(vm, -5);
667  sq_push(vm, -5);
668 
669  /* Call the script load function. sq_call removes the arguments (but not the
670  * function pointer) from the stack. */
671  if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
672 
673  /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
674  sq_pop(vm, 4);
675  return true;
676 }
677 
679 {
680  return this->engine->GetOpsTillSuspend();
681 }
682 
683 bool ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd)
684 {
685  ScriptObject::ActiveInstance active(this);
686 
687  if (!ScriptObject::CheckLastCommand(tile, p1, p2, cmd)) {
688  DEBUG(script, 1, "DoCommandCallback terminating a script, last command does not match expected command");
689  return false;
690  }
691 
692  ScriptObject::SetLastCommandRes(result.Succeeded());
693 
694  if (result.Failed()) {
695  ScriptObject::SetLastError(ScriptError::StringToError(result.GetErrorMessage()));
696  } else {
697  ScriptObject::IncreaseDoCommandCosts(result.GetCost());
698  ScriptObject::SetLastCost(result.GetCost());
699  }
700 
701  ScriptObject::SetLastCommand(INVALID_TILE, 0, 0, CMD_END);
702 
703  return true;
704 }
705 
706 void ScriptInstance::InsertEvent(class ScriptEvent *event)
707 {
708  ScriptObject::ActiveInstance active(this);
709 
710  ScriptEventController::InsertEvent(event);
711 }
712 
713 size_t ScriptInstance::GetAllocatedMemory() const
714 {
715  if (this->engine == nullptr) return this->last_allocated_memory;
716  return this->engine->GetAllocatedMemory();
717 }
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:20
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:81
void * event_data
Pointer to the event data storage.
bool HasScriptCrashed()
Find out if the squirrel script made an error before.
Definition: squirrel.cpp:737
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:99
Subdirectory
The different kinds of subdirectories OpenTTD uses.
Definition: fileio_type.h:110
void ThrowError(const char *error)
Throw a Squirrel error that will be nicely displayed to the user.
Definition: squirrel.hpp:238
bool CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend)
Call a method of an instance, in various flavors.
Definition: squirrel.cpp:347
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:409
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:341
SQInteger GetOpsTillSuspend()
How many operations can we execute till suspension?
Definition: squirrel.cpp:753
class ScriptStorage * GetStorage()
Get the storage of this script.
#define FOR_ALL_SEARCHPATHS(sp)
Iterator for all the search paths.
Definition: fileio_func.h:49
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:128
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:742
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:727
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
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:459
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:84
Searchpath
Types of searchpaths OpenTTD might use.
Definition: fileio_type.h:133
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:718
Common return value for all commands.
Definition: command_type.h:25
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:997
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:142
void ResumeError()
Resume the VM with an error so it prints a stack trace.
Definition: squirrel.cpp:334
bool Succeeded() const
Did this command succeed?
Definition: command_type.h:152
bool FileExists(const char *filename)
Test whether the given filename exists.
Definition: fileio.cpp:326
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:732
bool IsPaused()
Checks if the script is paused.
bool Failed() const
Did this command fail?
Definition: command_type.h:161
HSQUIRRELVM GetVM()
Get the squirrel VM.
Definition: squirrel.hpp:82
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:37
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:653
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:336
static const int MAX_SL_OPS
The maximum number of operations for saving or loading the data of a script.
Definition: script_info.hpp:21
uint32 TileIndex
The index/ID of a Tile.
Definition: tile_type.h:80
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:223
bool MethodExists(HSQOBJECT instance, const char *method_name)
Check if a method exists in an instance.
Definition: squirrel.cpp:294
void SlObject(void *object, const SaveLoad *sld)
Main SaveLoad function.
Definition: saveload.cpp:1548
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:47
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:23
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.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:131
SaveLoad type struct.
Definition: saveload.h:498
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:85
bool LoadScript(const char *script)
Load a script.
Definition: squirrel.cpp:680
void ReleaseObject(HSQOBJECT *ptr)
Release a SQ object.
Definition: squirrel.hpp:243
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:312
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:233