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 
8 /** @file script_instance.hpp The ScriptInstance tracks a script. */
9 
10 #ifndef SCRIPT_INSTANCE_HPP
11 #define SCRIPT_INSTANCE_HPP
12 
13 #include <squirrel.h>
14 #include "script_suspend.hpp"
15 
16 #include "../command_type.h"
17 #include "../company_type.h"
18 #include "../fileio_type.h"
19 
20 static const uint SQUIRREL_MAX_DEPTH = 25; ///< The maximum recursive depth for items stored in the savegame.
21 
22 /** Runtime information about a script like a pointer to the squirrel vm and the current state. */
23 class ScriptInstance {
24 public:
25 	friend class ScriptObject;
26 	friend class ScriptController;
27 
28 	/**
29 	 * Create a new script.
30 	 */
31 	ScriptInstance(const char *APIName);
32 	virtual ~ScriptInstance();
33 
34 	/**
35 	 * Initialize the script and prepare it for its first run.
36 	 * @param main_script The full path of the script to load.
37 	 * @param instance_name The name of the instance out of the script to load.
38 	 * @param company Which company this script is serving.
39 	 */
40 	void Initialize(const char *main_script, const char *instance_name, CompanyID company);
41 
42 	/**
43 	 * Get the value of a setting of the current instance.
44 	 * @param name The name of the setting.
45 	 * @return the value for the setting, or -1 if the setting is not known.
46 	 */
47 	virtual int GetSetting(const char *name) = 0;
48 
49 	/**
50 	 * Find a library.
51 	 * @param library The library name to find.
52 	 * @param version The version the library should have.
53 	 * @return The library if found, nullptr otherwise.
54 	 */
55 	virtual class ScriptInfo *FindLibrary(const char *library, int version) = 0;
56 
57 	/**
58 	 * A script in multiplayer waits for the server to handle its DoCommand.
59 	 *  It keeps waiting for this until this function is called.
60 	 */
61 	void Continue();
62 
63 	/**
64 	 * Run the GameLoop of a script.
65 	 */
66 	void GameLoop();
67 
68 	/**
69 	 * Let the VM collect any garbage.
70 	 */
71 	void CollectGarbage();
72 
73 	/**
74 	 * Get the storage of this script.
75 	 */
76 	class ScriptStorage *GetStorage();
77 
78 	/**
79 	 * Get the log pointer of this script.
80 	 */
81 	void *GetLogPointer();
82 
83 	/**
84 	 * Return a true/false reply for a DoCommand.
85 	 */
86 	static void DoCommandReturn(ScriptInstance *instance);
87 
88 	/**
89 	 * Return a VehicleID reply for a DoCommand.
90 	 */
91 	static void DoCommandReturnVehicleID(ScriptInstance *instance);
92 
93 	/**
94 	 * Return a SignID reply for a DoCommand.
95 	 */
96 	static void DoCommandReturnSignID(ScriptInstance *instance);
97 
98 	/**
99 	 * Return a GroupID reply for a DoCommand.
100 	 */
101 	static void DoCommandReturnGroupID(ScriptInstance *instance);
102 
103 	/**
104 	 * Return a GoalID reply for a DoCommand.
105 	 */
106 	static void DoCommandReturnGoalID(ScriptInstance *instance);
107 
108 	/**
109 	 * Return a StoryPageID reply for a DoCommand.
110 	 */
111 	static void DoCommandReturnStoryPageID(ScriptInstance *instance);
112 
113 	/**
114 	 * Return a StoryPageElementID reply for a DoCommand.
115 	 */
116 	static void DoCommandReturnStoryPageElementID(ScriptInstance *instance);
117 
118 	/**
119 	 * Get the controller attached to the instance.
120 	 */
GetController()121 	class ScriptController *GetController() { return controller; }
122 
123 	/**
124 	 * Return the "this script died" value
125 	 */
IsDead() const126 	inline bool IsDead() const { return this->is_dead; }
127 
128 	/**
129 	 * Call the script Save function and save all data in the savegame.
130 	 */
131 	void Save();
132 
133 	/**
134 	 * Don't save any data in the savegame.
135 	 */
136 	static void SaveEmpty();
137 
138 	/**
139 	 * Load data from a savegame and store it on the stack.
140 	 * @param version The version of the script when saving, or -1 if this was
141 	 *  not the original script saving the game.
142 	 */
143 	void Load(int version);
144 
145 	/**
146 	 * Load and discard data from a savegame.
147 	 */
148 	static void LoadEmpty();
149 
150 	/**
151 	 * Suspends the script for the current tick and then pause the execution
152 	 * of script. The script will not be resumed from its suspended state
153 	 * until the script has been unpaused.
154 	 */
155 	void Pause();
156 
157 	/**
158 	 * Checks if the script is paused.
159 	 * @return true if the script is paused, otherwise false
160 	 */
161 	bool IsPaused();
162 
163 	/**
164 	 * Resume execution of the script. This function will not actually execute
165 	 * the script, but set a flag so that the script is executed my the usual
166 	 * mechanism that executes the script.
167 	 */
168 	void Unpause();
169 
170 	/**
171 	 * Get the number of operations the script can execute before being suspended.
172 	 * This function is safe to call from within a function called by the script.
173 	 * @return The number of operations to execute.
174 	 */
175 	SQInteger GetOpsTillSuspend();
176 
177 	/**
178 	 * DoCommand callback function for all commands executed by scripts.
179 	 * @param result The result of the command.
180 	 * @param tile The tile on which the command was executed.
181 	 * @param p1 p1 as given to DoCommandPInternal.
182 	 * @param p2 p2 as given to DoCommandPInternal.
183 	 * @param cmd cmd as given to DoCommandPInternal.
184 	 * @return true if we handled result.
185 	 */
186 	bool DoCommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd);
187 
188 	/**
189 	 * Insert an event for this script.
190 	 * @param event The event to insert.
191 	 */
192 	void InsertEvent(class ScriptEvent *event);
193 
194 	/**
195 	 * Check if the instance is sleeping, which either happened because the
196 	 *  script executed a DoCommand, executed this.Sleep() or it has been
197 	 *  paused.
198 	 */
IsSleeping()199 	bool IsSleeping() { return this->suspend != 0; }
200 
201 	size_t GetAllocatedMemory() const;
202 
203 	/**
204 	 * Indicate whether this instance is currently being destroyed.
205 	 */
InShutdown() const206 	inline bool InShutdown() const { return this->in_shutdown; }
207 
208 	/**
209 	 * Decrease the ref count of a squirrel object.
210 	 * @param obj The object to release.
211 	 **/
212 	void ReleaseSQObject(HSQOBJECT *obj);
213 
214 protected:
215 	class Squirrel *engine;               ///< A wrapper around the squirrel vm.
216 	const char *versionAPI;               ///< Current API used by this script.
217 
218 	/**
219 	 * Register all API functions to the VM.
220 	 */
221 	virtual void RegisterAPI();
222 
223 	/**
224 	 * Load squirrel scripts to emulate an older API.
225 	 * @param api_version: API version to load scripts for
226 	 * @param dir Subdirectory to find the scripts in
227 	 * @return true iff script loading should proceed
228 	 */
229 	bool LoadCompatibilityScripts(const char *api_version, Subdirectory dir);
230 
231 	/**
232 	 * Tell the script it died.
233 	 */
234 	virtual void Died();
235 
236 	/**
237 	 * Get the callback handling DoCommands in case of networking.
238 	 */
239 	virtual CommandCallback *GetDoCommandCallback() = 0;
240 
241 	/**
242 	 * Load the dummy script.
243 	 */
244 	virtual void LoadDummyScript() = 0;
245 
246 private:
247 	class ScriptController *controller;   ///< The script main class.
248 	class ScriptStorage *storage;         ///< Some global information for each running script.
249 	SQObject *instance;                   ///< Squirrel-pointer to the script main class.
250 
251 	bool is_started;                      ///< Is the scripts constructor executed?
252 	bool is_dead;                         ///< True if the script has been stopped.
253 	bool is_save_data_on_stack;           ///< Is the save data still on the squirrel stack?
254 	int suspend;                          ///< The amount of ticks to suspend this script before it's allowed to continue.
255 	bool is_paused;                       ///< Is the script paused? (a paused script will not be executed until unpaused)
256 	bool in_shutdown;                     ///< Is this instance currently being destructed?
257 	Script_SuspendCallbackProc *callback; ///< Callback that should be called in the next tick the script runs.
258 	size_t last_allocated_memory;         ///< Last known allocated memory value (for display for crashed scripts)
259 
260 	/**
261 	 * Call the script Load function if it exists and data was loaded
262 	 *  from a savegame.
263 	 */
264 	bool CallLoad();
265 
266 	/**
267 	 * Save one object (int / string / array / table) to the savegame.
268 	 * @param vm The virtual machine to get all the data from.
269 	 * @param index The index on the squirrel stack of the element to save.
270 	 * @param max_depth The maximum depth recursive arrays / tables will be stored
271 	 *   with before an error is returned.
272 	 * @param test If true, don't really store the data but only check if it is
273 	 *   valid.
274 	 * @return True if the saving was successful.
275 	 */
276 	static bool SaveObject(HSQUIRRELVM vm, SQInteger index, int max_depth, bool test);
277 
278 	/**
279 	 * Load all objects from a savegame.
280 	 * @return True if the loading was successful.
281 	 */
282 	static bool LoadObjects(HSQUIRRELVM vm);
283 };
284 
285 #endif /* SCRIPT_INSTANCE_HPP */
286