12 #include "../stdafx.h" 14 #include "../saveload/saveload.h" 16 #include "../script/squirrel_class.hpp" 23 #include "api/script_controller.hpp" 24 #include "api/script_error.hpp" 25 #include "api/script_event.hpp" 26 #include "api/script_log.hpp" 28 #include "../company_base.h" 29 #include "../company_func.h" 30 #include "../fileio_func.h" 32 #include "../safeguards.h" 34 ScriptStorage::~ScriptStorage()
37 if (
event_data !=
nullptr) ScriptEventController::FreeEventPointer();
38 if (
log_data !=
nullptr) ScriptLog::FreeLogPointer();
46 static void PrintFunc(
bool error_msg,
const SQChar *message)
49 ScriptController::Print(error_msg, message);
60 is_save_data_on_stack(false),
72 ScriptObject::ActiveInstance active(
this);
74 this->
controller =
new ScriptController(company);
81 ScriptObject::SetAllowDoCommand(
false);
83 if (strcmp(main_script,
"%_dummy") == 0) {
86 if (this->
engine->
IsSuspended()) ScriptLog::Error(
"This script took too long to load script. AI is not started.");
92 this->
instance = MallocT<SQObject>(1);
97 ScriptObject::SetAllowDoCommand(
true);
114 char script_name[32];
115 seprintf(script_name,
lastof(script_name),
"compat_%s.nut", api_version);
119 FioAppendDirectory(buf,
lastof(buf), sp, dir);
125 ScriptLog::Error(
"Failed to load API compatibility script");
126 DEBUG(script, 0,
"Error compiling / running API compatibility script: %s", buf);
130 ScriptLog::Warning(
"API compatibility script not found");
134 ScriptInstance::~ScriptInstance()
136 ScriptObject::ActiveInstance active(
this);
153 DEBUG(script, 0,
"The script died unexpectedly.");
166 ScriptObject::ActiveInstance active(
this);
168 if (this->
IsDead())
return;
179 if (--this->
suspend > 0)
return;
204 ScriptObject::SetAllowDoCommand(
false);
208 if (this->
engine->
IsSuspended()) ScriptLog::Error(
"This script took too long to initialize. Script is not started.");
214 if (this->
engine->
IsSuspended()) ScriptLog::Error(
"This script took too long in the Load function. Script is not started.");
218 ScriptObject::SetAllowDoCommand(
true);
260 instance->
engine->InsertResult(ScriptObject::GetLastCommandRes());
265 instance->
engine->InsertResult(ScriptObject::GetNewVehicleID());
270 instance->
engine->InsertResult(ScriptObject::GetNewSignID());
275 instance->
engine->InsertResult(ScriptObject::GetNewGroupID());
280 instance->
engine->InsertResult(ScriptObject::GetNewGoalID());
285 instance->
engine->InsertResult(ScriptObject::GetNewStoryPageID());
290 instance->
engine->InsertResult(ScriptObject::GetNewStoryPageElementID());
300 ScriptObject::ActiveInstance active(
this);
302 return ScriptObject::GetLogPointer();
341 SLEG_VAR(_script_sl_byte, SLE_UINT8),
347 if (max_depth == 0) {
348 ScriptLog::Error(
"Savedata can only be nested to 25 deep. No data saved.");
352 switch (sq_gettype(vm, index)) {
359 sq_getinteger(vm, index, &res);
361 int value = (int)res;
373 sq_getstring(vm, index, &buf);
374 size_t len = strlen(buf) + 1;
376 ScriptLog::Error(
"Maximum string length is 254 chars. No data saved.");
380 _script_sl_byte = (byte)len;
382 SlArray(const_cast<char *>(buf), len, SLE_CHAR);
393 while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
395 bool res =
SaveObject(vm, -1, max_depth - 1, test);
416 while (SQ_SUCCEEDED(sq_next(vm, index - 1))) {
439 sq_getbool(vm, index, &res);
441 _script_sl_byte = res ? 1 : 0;
456 ScriptLog::Error(
"You tried to save an unsupported type. No data saved.");
469 ScriptObject::ActiveInstance active(
this);
489 bool backup_allow = ScriptObject::GetAllowDoCommand();
490 ScriptObject::SetAllowDoCommand(
false);
512 ScriptObject::SetAllowDoCommand(backup_allow);
514 if (!sq_istable(savedata)) {
515 ScriptLog::Error(this->
engine->
IsSuspended() ?
"This script took too long to Save." :
"Save function should return a table.");
520 sq_pushobject(vm, savedata);
531 ScriptLog::Warning(
"Save function is not implemented");
559 switch (_script_sl_byte) {
563 if (vm !=
nullptr) sq_pushinteger(vm, (SQInteger)value);
569 static char buf[256];
570 SlArray(buf, _script_sl_byte, SLE_CHAR);
571 if (vm !=
nullptr) sq_pushstring(vm, buf, -1);
576 if (vm !=
nullptr) sq_newarray(vm, 0);
578 if (vm !=
nullptr) sq_arrayappend(vm, -2);
585 if (vm !=
nullptr) sq_newtable(vm);
588 if (vm !=
nullptr) sq_rawset(vm, -3);
596 if (vm !=
nullptr) sq_pushbool(vm, (SQBool)(_script_sl_byte != 0));
601 if (vm !=
nullptr) sq_pushnull(vm);
609 default: NOT_REACHED();
617 if (_script_sl_byte == 0)
return;
624 ScriptObject::ActiveInstance active(
this);
626 if (this->
engine ==
nullptr || version == -1) {
634 if (_script_sl_byte == 0)
return;
636 sq_pushinteger(vm, version);
650 ScriptLog::Warning(
"Loading failed: there was data for the script to load, but the script does not have a Load() function.");
660 sq_pushstring(vm,
"Load", -1);
671 if (SQ_FAILED(sq_call(vm, 3, SQFalse, SQFalse,
MAX_SL_OPS)))
return false;
685 ScriptObject::ActiveInstance active(
this);
687 if (!ScriptObject::CheckLastCommand(tile, p1, p2, cmd)) {
688 DEBUG(script, 1,
"DoCommandCallback terminating a script, last command does not match expected command");
692 ScriptObject::SetLastCommandRes(result.
Succeeded());
695 ScriptObject::SetLastError(ScriptError::StringToError(result.
GetErrorMessage()));
697 ScriptObject::IncreaseDoCommandCosts(result.
GetCost());
698 ScriptObject::SetLastCost(result.
GetCost());
708 ScriptObject::ActiveInstance active(
this);
710 ScriptEventController::InsertEvent(event);
713 size_t ScriptInstance::GetAllocatedMemory()
const 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.
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.
void * event_data
Pointer to the event data storage.
bool HasScriptCrashed()
Find out if the squirrel script made an error before.
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.
Subdirectory
The different kinds of subdirectories OpenTTD uses.
void ThrowError(const char *error)
Throw a Squirrel error that will be nicely displayed to the user.
bool CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend)
Call a method of an instance, in various flavors.
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:
int GetSuspendTime()
Get the amount of ticks the script should be suspended.
void CollectGarbage()
Tell the VM to do a garbage collection run.
SQInteger GetOpsTillSuspend()
How many operations can we execute till suspension?
class ScriptStorage * GetStorage()
Get the storage of this script.
#define FOR_ALL_SEARCHPATHS(sp)
Iterator for all the search paths.
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.
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.
bool is_paused
Is the script paused? (a paused script will not be executed until unpaused)
static void DecreaseOps(HSQUIRRELVM vm, int amount)
Tell the VM to remove amount ops from the number of ops till suspend.
#define lastof(x)
Get the last element of an fixed size array.
bool CreateClassInstance(const char *class_name, void *real_instance, HSQOBJECT *instance)
Exactly the same as CreateClassInstanceVM, only callable without instance of Squirrel.
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.
Searchpath
Types of searchpaths OpenTTD might use.
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.
Common return value for all commands.
static void DoCommandReturnStoryPageElementID(ScriptInstance *instance)
Return a StoryPageElementID reply for a DoCommand.
int suspend
The amount of ticks to suspend this script before it'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.
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.
void ResumeError()
Resume the VM with an error so it prints a stack trace.
bool Succeeded() const
Did this command succeed?
bool FileExists(const char *filename)
Test whether the given filename exists.
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.
bool IsPaused()
Checks if the script is paused.
bool Failed() const
Did this command fail?
HSQUIRRELVM GetVM()
Get the squirrel VM.
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.
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.
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'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)
static const int MAX_SL_OPS
The maximum number of operations for saving or loading the data of a script.
uint32 TileIndex
The index/ID of a Tile.
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.
bool MethodExists(HSQOBJECT instance, const char *method_name)
Check if a method exists in an instance.
void SlObject(void *object, const SaveLoad *sld)
Main SaveLoad function.
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.
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.
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.
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.
bool LoadScript(const char *script)
Load a script.
void ReleaseObject(HSQOBJECT *ptr)
Release a SQ object.
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.
void GameLoop()
Run the GameLoop of a script.
class Squirrel * engine
A wrapper around the squirrel vm.
class Squirrel * engine
The engine we'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.