OpenTTD
script_instance.cpp
Go to the documentation of this file.
1 /* $Id: script_instance.cpp 26785 2014-09-07 09:30:57Z rubidium $ */
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 != NULL) ScriptEventController::FreeEventPointer();
38  if (log_data != NULL) 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(NULL),
54  versionAPI(NULL),
55  controller(NULL),
56  storage(NULL),
57  instance(NULL),
58  is_started(false),
59  is_dead(false),
60  is_save_data_on_stack(false),
61  suspend(0),
62  is_paused(false),
63  callback(NULL)
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 != NULL) this->engine->ReleaseObject(this->instance);
139  if (engine != NULL) 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  if (this->instance != NULL) this->engine->ReleaseObject(this->instance);
157  delete this->engine;
158  this->instance = NULL;
159  this->engine = NULL;
160 }
161 
163 {
164  ScriptObject::ActiveInstance active(this);
165 
166  if (this->IsDead()) return;
167  if (this->engine->HasScriptCrashed()) {
168  /* The script crashed during saving, kill it here. */
169  this->Died();
170  return;
171  }
172  if (this->is_paused) return;
173  this->controller->ticks++;
174 
175  if (this->suspend < -1) this->suspend++; // Multiplayer suspend, increase up to -1.
176  if (this->suspend < 0) return; // Multiplayer suspend, wait for Continue().
177  if (--this->suspend > 0) return; // Singleplayer suspend, decrease to 0.
178 
179  _current_company = ScriptObject::GetCompany();
180 
181  /* If there is a callback to call, call that first */
182  if (this->callback != NULL) {
183  if (this->is_save_data_on_stack) {
184  sq_poptop(this->engine->GetVM());
185  this->is_save_data_on_stack = false;
186  }
187  try {
188  this->callback(this);
189  } catch (Script_Suspend e) {
190  this->suspend = e.GetSuspendTime();
191  this->callback = e.GetSuspendCallback();
192 
193  return;
194  }
195  }
196 
197  this->suspend = 0;
198  this->callback = NULL;
199 
200  if (!this->is_started) {
201  try {
202  ScriptObject::SetAllowDoCommand(false);
203  /* Run the constructor if it exists. Don't allow any DoCommands in it. */
204  if (this->engine->MethodExists(*this->instance, "constructor")) {
205  if (!this->engine->CallMethod(*this->instance, "constructor", MAX_CONSTRUCTOR_OPS) || this->engine->IsSuspended()) {
206  if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long to initialize. Script is not started.");
207  this->Died();
208  return;
209  }
210  }
211  if (!this->CallLoad() || this->engine->IsSuspended()) {
212  if (this->engine->IsSuspended()) ScriptLog::Error("This script took too long in the Load function. Script is not started.");
213  this->Died();
214  return;
215  }
216  ScriptObject::SetAllowDoCommand(true);
217  /* Start the script by calling Start() */
218  if (!this->engine->CallMethod(*this->instance, "Start", _settings_game.script.script_max_opcode_till_suspend) || !this->engine->IsSuspended()) this->Died();
219  } catch (Script_Suspend e) {
220  this->suspend = e.GetSuspendTime();
221  this->callback = e.GetSuspendCallback();
222  } catch (Script_FatalError e) {
223  this->is_dead = true;
224  this->engine->ThrowError(e.GetErrorMessage());
225  this->engine->ResumeError();
226  this->Died();
227  }
228 
229  this->is_started = true;
230  return;
231  }
232  if (this->is_save_data_on_stack) {
233  sq_poptop(this->engine->GetVM());
234  this->is_save_data_on_stack = false;
235  }
236 
237  /* Continue the VM */
238  try {
240  } catch (Script_Suspend e) {
241  this->suspend = e.GetSuspendTime();
242  this->callback = e.GetSuspendCallback();
243  } catch (Script_FatalError e) {
244  this->is_dead = true;
245  this->engine->ThrowError(e.GetErrorMessage());
246  this->engine->ResumeError();
247  this->Died();
248  }
249 }
250 
252 {
253  if (this->is_started && !this->IsDead()) this->engine->CollectGarbage();
254 }
255 
257 {
258  instance->engine->InsertResult(ScriptObject::GetLastCommandRes());
259 }
260 
262 {
263  instance->engine->InsertResult(ScriptObject::GetNewVehicleID());
264 }
265 
267 {
268  instance->engine->InsertResult(ScriptObject::GetNewSignID());
269 }
270 
272 {
273  instance->engine->InsertResult(ScriptObject::GetNewGroupID());
274 }
275 
277 {
278  instance->engine->InsertResult(ScriptObject::GetNewGoalID());
279 }
280 
282 {
283  instance->engine->InsertResult(ScriptObject::GetNewStoryPageID());
284 }
285 
287 {
288  instance->engine->InsertResult(ScriptObject::GetNewStoryPageElementID());
289 }
290 
292 {
293  return this->storage;
294 }
295 
297 {
298  ScriptObject::ActiveInstance active(this);
299 
301 }
302 
303 /*
304  * All data is stored in the following format:
305  * First 1 byte indicating if there is a data blob at all.
306  * 1 byte indicating the type of data.
307  * The data itself, this differs per type:
308  * - integer: a binary representation of the integer (int32).
309  * - string: First one byte with the string length, then a 0-terminated char
310  * array. The string can't be longer than 255 bytes (including
311  * terminating '\0').
312  * - array: All data-elements of the array are saved recursive in this
313  * format, and ended with an element of the type
314  * SQSL_ARRAY_TABLE_END.
315  * - table: All key/value pairs are saved in this format (first key 1, then
316  * value 1, then key 2, etc.). All keys and values can have an
317  * arbitrary type (as long as it is supported by the save function
318  * of course). The table is ended with an element of the type
319  * SQSL_ARRAY_TABLE_END.
320  * - bool: A single byte with value 1 representing true and 0 false.
321  * - null: No data.
322  */
323 
326  SQSL_INT = 0x00,
327  SQSL_STRING = 0x01,
328  SQSL_ARRAY = 0x02,
329  SQSL_TABLE = 0x03,
330  SQSL_BOOL = 0x04,
331  SQSL_NULL = 0x05,
333 };
334 
335 static byte _script_sl_byte;
336 
338 static const SaveLoad _script_byte[] = {
339  SLEG_VAR(_script_sl_byte, SLE_UINT8),
340  SLE_END()
341 };
342 
343 /* static */ bool ScriptInstance::SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test)
344 {
345  if (max_depth == 0) {
346  ScriptLog::Error("Savedata can only be nested to 25 deep. No data saved."); // SQUIRREL_MAX_DEPTH = 25
347  return false;
348  }
349 
350  switch (sq_gettype(vm, index)) {
351  case OT_INTEGER: {
352  if (!test) {
353  _script_sl_byte = SQSL_INT;
354  SlObject(NULL, _script_byte);
355  }
356  SQInteger res;
357  sq_getinteger(vm, index, &res);
358  if (!test) {
359  int value = (int)res;
360  SlArray(&value, 1, SLE_INT32);
361  }
362  return true;
363  }
364 
365  case OT_STRING: {
366  if (!test) {
367  _script_sl_byte = SQSL_STRING;
368  SlObject(NULL, _script_byte);
369  }
370  const SQChar *buf;
371  sq_getstring(vm, index, &buf);
372  size_t len = strlen(buf) + 1;
373  if (len >= 255) {
374  ScriptLog::Error("Maximum string length is 254 chars. No data saved.");
375  return false;
376  }
377  if (!test) {
378  _script_sl_byte = (byte)len;
379  SlObject(NULL, _script_byte);
380  SlArray(const_cast<char *>(buf), len, SLE_CHAR);
381  }
382  return true;
383  }
384 
385  case OT_ARRAY: {
386  if (!test) {
387  _script_sl_byte = SQSL_ARRAY;
388  SlObject(NULL, _script_byte);
389  }
390  sq_pushnull(vm);
391  while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
392  /* Store the value */
393  bool res = SaveObject(vm, -1, max_depth - 1, test);
394  sq_pop(vm, 2);
395  if (!res) {
396  sq_pop(vm, 1);
397  return false;
398  }
399  }
400  sq_pop(vm, 1);
401  if (!test) {
402  _script_sl_byte = SQSL_ARRAY_TABLE_END;
403  SlObject(NULL, _script_byte);
404  }
405  return true;
406  }
407 
408  case OT_TABLE: {
409  if (!test) {
410  _script_sl_byte = SQSL_TABLE;
411  SlObject(NULL, _script_byte);
412  }
413  sq_pushnull(vm);
414  while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
415  /* Store the key + value */
416  bool res = SaveObject(vm, -2, max_depth - 1, test) && SaveObject(vm, -1, max_depth - 1, test);
417  sq_pop(vm, 2);
418  if (!res) {
419  sq_pop(vm, 1);
420  return false;
421  }
422  }
423  sq_pop(vm, 1);
424  if (!test) {
425  _script_sl_byte = SQSL_ARRAY_TABLE_END;
426  SlObject(NULL, _script_byte);
427  }
428  return true;
429  }
430 
431  case OT_BOOL: {
432  if (!test) {
433  _script_sl_byte = SQSL_BOOL;
434  SlObject(NULL, _script_byte);
435  }
436  SQBool res;
437  sq_getbool(vm, index, &res);
438  if (!test) {
439  _script_sl_byte = res ? 1 : 0;
440  SlObject(NULL, _script_byte);
441  }
442  return true;
443  }
444 
445  case OT_NULL: {
446  if (!test) {
447  _script_sl_byte = SQSL_NULL;
448  SlObject(NULL, _script_byte);
449  }
450  return true;
451  }
452 
453  default:
454  ScriptLog::Error("You tried to save an unsupported type. No data saved.");
455  return false;
456  }
457 }
458 
459 /* static */ void ScriptInstance::SaveEmpty()
460 {
461  _script_sl_byte = 0;
462  SlObject(NULL, _script_byte);
463 }
464 
466 {
467  ScriptObject::ActiveInstance active(this);
468 
469  /* Don't save data if the script didn't start yet or if it crashed. */
470  if (this->engine == NULL || this->engine->HasScriptCrashed()) {
471  SaveEmpty();
472  return;
473  }
474 
475  HSQUIRRELVM vm = this->engine->GetVM();
476  if (this->is_save_data_on_stack) {
477  _script_sl_byte = 1;
478  SlObject(NULL, _script_byte);
479  /* Save the data that was just loaded. */
480  SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
481  } else if (!this->is_started) {
482  SaveEmpty();
483  return;
484  } else if (this->engine->MethodExists(*this->instance, "Save")) {
485  HSQOBJECT savedata;
486  /* We don't want to be interrupted during the save function. */
487  bool backup_allow = ScriptObject::GetAllowDoCommand();
488  ScriptObject::SetAllowDoCommand(false);
489  try {
490  if (!this->engine->CallMethod(*this->instance, "Save", &savedata, MAX_SL_OPS)) {
491  /* The script crashed in the Save function. We can't kill
492  * it here, but do so in the next script tick. */
493  SaveEmpty();
494  this->engine->CrashOccurred();
495  return;
496  }
497  } catch (Script_FatalError e) {
498  /* If we don't mark the script as dead here cleaning up the squirrel
499  * stack could throw Script_FatalError again. */
500  this->is_dead = true;
501  this->engine->ThrowError(e.GetErrorMessage());
502  this->engine->ResumeError();
503  SaveEmpty();
504  /* We can't kill the script here, so mark it as crashed (not dead) and
505  * kill it in the next script tick. */
506  this->is_dead = false;
507  this->engine->CrashOccurred();
508  return;
509  }
510  ScriptObject::SetAllowDoCommand(backup_allow);
511 
512  if (!sq_istable(savedata)) {
513  ScriptLog::Error(this->engine->IsSuspended() ? "This script took too long to Save." : "Save function should return a table.");
514  SaveEmpty();
515  this->engine->CrashOccurred();
516  return;
517  }
518  sq_pushobject(vm, savedata);
519  if (SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, true)) {
520  _script_sl_byte = 1;
521  SlObject(NULL, _script_byte);
522  SaveObject(vm, -1, SQUIRREL_MAX_DEPTH, false);
523  this->is_save_data_on_stack = true;
524  } else {
525  SaveEmpty();
526  this->engine->CrashOccurred();
527  }
528  } else {
529  ScriptLog::Warning("Save function is not implemented");
530  _script_sl_byte = 0;
531  SlObject(NULL, _script_byte);
532  }
533 }
534 
536 {
537  /* Suspend script. */
538  HSQUIRRELVM vm = this->engine->GetVM();
540 
541  this->is_paused = true;
542 }
543 
545 {
546  this->is_paused = false;
547 }
548 
550 {
551  return this->is_paused;
552 }
553 
554 /* static */ bool ScriptInstance::LoadObjects(HSQUIRRELVM vm)
555 {
556  SlObject(NULL, _script_byte);
557  switch (_script_sl_byte) {
558  case SQSL_INT: {
559  int value;
560  SlArray(&value, 1, SLE_INT32);
561  if (vm != NULL) sq_pushinteger(vm, (SQInteger)value);
562  return true;
563  }
564 
565  case SQSL_STRING: {
566  SlObject(NULL, _script_byte);
567  static char buf[256];
568  SlArray(buf, _script_sl_byte, SLE_CHAR);
569  if (vm != NULL) sq_pushstring(vm, buf, -1);
570  return true;
571  }
572 
573  case SQSL_ARRAY: {
574  if (vm != NULL) sq_newarray(vm, 0);
575  while (LoadObjects(vm)) {
576  if (vm != NULL) sq_arrayappend(vm, -2);
577  /* The value is popped from the stack by squirrel. */
578  }
579  return true;
580  }
581 
582  case SQSL_TABLE: {
583  if (vm != NULL) sq_newtable(vm);
584  while (LoadObjects(vm)) {
585  LoadObjects(vm);
586  if (vm != NULL) sq_rawset(vm, -3);
587  /* The key (-2) and value (-1) are popped from the stack by squirrel. */
588  }
589  return true;
590  }
591 
592  case SQSL_BOOL: {
593  SlObject(NULL, _script_byte);
594  if (vm != NULL) sq_pushbool(vm, (SQBool)(_script_sl_byte != 0));
595  return true;
596  }
597 
598  case SQSL_NULL: {
599  if (vm != NULL) sq_pushnull(vm);
600  return true;
601  }
602 
603  case SQSL_ARRAY_TABLE_END: {
604  return false;
605  }
606 
607  default: NOT_REACHED();
608  }
609 }
610 
611 /* static */ void ScriptInstance::LoadEmpty()
612 {
613  SlObject(NULL, _script_byte);
614  /* Check if there was anything saved at all. */
615  if (_script_sl_byte == 0) return;
616 
617  LoadObjects(NULL);
618 }
619 
620 void ScriptInstance::Load(int version)
621 {
622  ScriptObject::ActiveInstance active(this);
623 
624  if (this->engine == NULL || version == -1) {
625  LoadEmpty();
626  return;
627  }
628  HSQUIRRELVM vm = this->engine->GetVM();
629 
630  SlObject(NULL, _script_byte);
631  /* Check if there was anything saved at all. */
632  if (_script_sl_byte == 0) return;
633 
634  sq_pushinteger(vm, version);
635  LoadObjects(vm);
636  this->is_save_data_on_stack = true;
637 }
638 
640 {
641  HSQUIRRELVM vm = this->engine->GetVM();
642  /* Is there save data that we should load? */
643  if (!this->is_save_data_on_stack) return true;
644  /* Whatever happens, after CallLoad the savegame data is removed from the stack. */
645  this->is_save_data_on_stack = false;
646 
647  if (!this->engine->MethodExists(*this->instance, "Load")) {
648  ScriptLog::Warning("Loading failed: there was data for the script to load, but the script does not have a Load() function.");
649 
650  /* Pop the savegame data and version. */
651  sq_pop(vm, 2);
652  return true;
653  }
654 
655  /* Go to the instance-root */
656  sq_pushobject(vm, *this->instance);
657  /* Find the function-name inside the script */
658  sq_pushstring(vm, "Load", -1);
659  /* Change the "Load" string in a function pointer */
660  sq_get(vm, -2);
661  /* Push the main instance as "this" object */
662  sq_pushobject(vm, *this->instance);
663  /* Push the version data and savegame data as arguments */
664  sq_push(vm, -5);
665  sq_push(vm, -5);
666 
667  /* Call the script load function. sq_call removes the arguments (but not the
668  * function pointer) from the stack. */
669  if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse, MAX_SL_OPS))) return false;
670 
671  /* Pop 1) The version, 2) the savegame data, 3) the object instance, 4) the function pointer. */
672  sq_pop(vm, 4);
673  return true;
674 }
675 
677 {
678  return this->engine->GetOpsTillSuspend();
679 }
680 
681 void ScriptInstance::DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
682 {
683  ScriptObject::ActiveInstance active(this);
684 
685  ScriptObject::SetLastCommandRes(result.Succeeded());
686 
687  if (result.Failed()) {
688  ScriptObject::SetLastError(ScriptError::StringToError(result.GetErrorMessage()));
689  } else {
690  ScriptObject::IncreaseDoCommandCosts(result.GetCost());
691  ScriptObject::SetLastCost(result.GetCost());
692  }
693 }
694 
695 void ScriptInstance::InsertEvent(class ScriptEvent *event)
696 {
697  ScriptObject::ActiveInstance active(this);
698 
700 }