1 #ifndef _SCRIPT_H_
2 #define _SCRIPT_H_
3 
4 /** @file script.h handles the virtual machine, interface to squirrel */
5 
6 #include "api_function.h"
7 #include "api_param.h"
8 #include "../simtypes.h"
9 #include "../squirrel/squirrel.h"
10 #include "../utils/plainstring.h"
11 #include <string>
12 
13 class log_t;
14 template<class key_t, class value_t> class inthashtable_tpl;
15 void sq_setwakeupretvalue(HSQUIRRELVM v); //sq_extensions
16 
17 /**
18  * Class providing interface to squirrel's virtual machine.
19  *
20  * Logs output to script.log.
21  * Opens error window in case of script errors.
22  */
23 class script_vm_t {
24 public:
25 	script_vm_t(const char* include_path, const char* log_name);
26 	~script_vm_t();
27 
28 	/**
29 	 * loads file, calls script
30 	 * @returns error msg (or NULL if succeeded)
31 	 */
32 	const char* call_script(const char* filename);
33 
34 	/**
35 	 * compiles and executes given string
36 	 * @returns error msg (or NULL if succeeded)
37 	 */
38 	const char* eval_string(const char* squirrel_string);
39 
uses_vm(HSQUIRRELVM other)40 	bool uses_vm(HSQUIRRELVM other) const { return vm == other  ||  thread == other; }
41 
get_vm()42 	const HSQUIRRELVM& get_vm() const { return vm; }
43 
get_error()44 	const char* get_error() const { return error_msg.c_str(); }
45 
46 	/**
47 	 * The script can only act as a certain player.
48 	 * @param player_nr the number of the player (PLAYER_UNOWNED for scenarios)
49 	 */
50 	void set_my_player(uint8 player_nr);
51 
52 	/// priority of function call
53 	enum call_type_t {
54 		FORCE,   ///< function has to return, raise error if not
55 		FORCEX,  ///< function has to return, raise error if not, give more opcodes
56 		QUEUE,   ///< function call can be queued, return value can be propagated by call back
57 		TRY,     ///< function call will not be queued, if virtual machine is suspended just return
58 	};
59 
60 	/**
61 	 * @param err error string returned by call_script
62 	 * @return whether the call was suspended
63 	 */
64 	static bool is_call_suspended(const char* err);
65 
66 #	define prep_function_call() \
67 		HSQUIRRELVM job; \
68 		const char* err = intern_prepare_call(job, ct, function); \
69 		if (err) { \
70 			return err; \
71 		} \
72 		int nparam = 1;
73 
74 #	define do_function_call() \
75 		err = intern_finish_call(job, ct, nparam, true); \
76 		if (err == NULL) { \
77 			ret = script_api::param<R>::get(job, -1); \
78 			sq_poptop(job); \
79 		} \
80 		return err;
81 
82 	/**
83 	 * calls scripted function
84 	 * @param function function name of squirrel function
85 	 * @returns error msg (or NULL if succeeded)
86 	 */
call_function(call_type_t ct,const char * function)87 	const char* call_function(call_type_t ct, const char* function) {
88 		prep_function_call();
89 		return intern_finish_call(job, ct, nparam, false);
90 	}
91 
92 	/**
93 	 * calls scripted function
94 	 *
95 	 * @tparam R type of return value
96 	 * @param function function name of squirrel function
97 	 * @param ret return value of script function is stored here
98 	 * @returns error msg (or NULL if succeeded), if call was suspended ret is invalid
99 	 */
100 	template<class R>
call_function(call_type_t ct,const char * function,R & ret)101 	const char* call_function(call_type_t ct, const char* function, R& ret) {
102 		prep_function_call();
103 		do_function_call();
104 	}
105 
106 	/**
107 	 * calls scripted function
108 	 *
109 	 * @tparam R type of return value
110 	 * @tparam A1 type of first argument
111 	 * @param function function name of squirrel function
112 	 * @param arg1 first argument passed to squirrel function
113 	 * @param ret return value of script function is stored here
114 	 * @returns error msg (or NULL if succeeded), if call was suspended ret is invalid
115 	 */
116 	template<class R, class A1>
call_function(call_type_t ct,const char * function,R & ret,A1 arg1)117 	const char* call_function(call_type_t ct, const char* function, R& ret, A1 arg1) {
118 		prep_function_call();
119 		script_api::param<A1>::push(job, arg1); nparam++;
120 		do_function_call();
121 	}
122 	template<class R, class A1, class A2>
call_function(call_type_t ct,const char * function,R & ret,A1 arg1,A2 arg2)123 	const char* call_function(call_type_t ct, const char* function, R& ret, A1 arg1, A2 arg2) {
124 		prep_function_call();
125 		script_api::param<A1>::push(job, arg1); nparam++;
126 		script_api::param<A2>::push(job, arg2); nparam++;
127 		do_function_call();
128 	}
129 	template<class R, class A1, class A2, class A3>
call_function(call_type_t ct,const char * function,R & ret,A1 arg1,A2 arg2,A3 arg3)130 	const char* call_function(call_type_t ct, const char* function, R& ret, A1 arg1, A2 arg2, A3 arg3) {
131 		prep_function_call();
132 		script_api::param<A1>::push(job, arg1); nparam++;
133 		script_api::param<A2>::push(job, arg2); nparam++;
134 		script_api::param<A3>::push(job, arg3); nparam++;
135 		do_function_call();
136 	}
137 
138 	/**
139 	 * Registers a c++ function to be available as callback.
140 	 * A callback is called when a function call got suspended, resumed, and returned something.
141 	 * @tparam F function signature
142 	 * @param F function pointer
143 	 * @param name register callback under this name
144 	 */
145 	template<typename F>
register_callback(F funcptr,const char * name)146 	void register_callback(F funcptr, const char* name)
147 	{
148 		sq_pushregistrytable(vm);
149 		script_api::register_method(vm, funcptr, name);
150 		sq_poptop(vm);
151 	}
152 
153 	/**
154 	 * Prepares a callback call.
155 	 * @param function name of function to be called as callback
156 	 * @param nret the nret-th parameter will be replaced by return value of suspended function.
157 	 */
158 	template<class A1, class A2>
prepare_callback(const char * function,int nret,A1 arg1,A2 arg2)159 	void prepare_callback(const char* function, int nret, A1 arg1, A2 arg2) {
160 		if (intern_prepare_pending_callback(function, nret))  {
161 			script_api::param<A1>::push(vm, arg1);
162 			script_api::param<A2>::push(vm, arg2);
163 			intern_store_pending_callback(3);
164 		}
165 	}
166 
167 	template<class A1, class A2, class A3>
prepare_callback(const char * function,int nret,A1 arg1,A2 arg2,A3 arg3)168 	void prepare_callback(const char* function, int nret, A1 arg1, A2 arg2, A3 arg3) {
169 		if (intern_prepare_pending_callback(function, nret))  {
170 			script_api::param<A1>::push(vm, arg1);
171 			script_api::param<A2>::push(vm, arg2);
172 			script_api::param<A3>::push(vm, arg3);
173 			intern_store_pending_callback(4);
174 		}
175 	}
176 
177 	/**
178 	 * Clears pending callback initialized by prepare_callback.
179 	 */
180 	void clear_pending_callback();
181 
182 private:
183 	/// virtual machine running everything
184 	HSQUIRRELVM vm;
185 
186 	/// thread in the virtual machine, used to run functions that can be suspended
187 	HSQUIRRELVM thread;
188 
189 	/// our log file
190 	log_t* log;
191 
192 	plainstring error_msg;
193 
194 	/// @{
195 	/// @name Helper functions to call, suspend, queue calls to scripted functions
196 
197 	/// prepare function call, used in templated call_function(), sets job to vm that should run function
198 	const char* intern_prepare_call(HSQUIRRELVM &job, call_type_t ct, const char* function);
199 
200 	/// actually call function, used in templated call_function(),
201 	/// does also: resume suspended call, queue current call
202 	const char* intern_finish_call(HSQUIRRELVM job, call_type_t ct, int nparams, bool retvalue);
203 
204 	/// queues current call
205 	static void intern_queue_call(HSQUIRRELVM job, int nparams, bool retvalue);
206 
207 	/// resumes a suspended call. calls callbacks.
208 	void intern_resume_call(HSQUIRRELVM job);
209 
210 	/// calls function. If it was a queued call, also calls callbacks.
211 	static const char* intern_call_function(HSQUIRRELVM job, call_type_t ct, int nparams, bool retvalue);
212 
213 	/// pops an queued call and puts it on the stack, also activates corresponding callbacks
214 	bool intern_prepare_queued(HSQUIRRELVM job, int &nparams, bool &retvalue);
215 
216 	/// prepare function call to callback, used in templated prepare_callback()
217 	bool intern_prepare_pending_callback(const char* function, sint32 nret);
218 
219 	/// saves the callback call (with arguments), used in templated prepare_callback()
220 	void intern_store_pending_callback(sint32 nparams);
221 
222 	/// pops callback from queued callbacks, and makes it active
223 	void intern_make_queued_callback_active();
224 
225 	/// takes pending callback, and makes it active
226 	void intern_make_pending_callback_active();
227 
228 	/// calls all active callbacks
229 	static void intern_call_callbacks(HSQUIRRELVM job);
230 
231 	/// @}
232 
233 	/// custom error handler for compile and runtime errors of squirrel scripts
234 	static void errorfunc(HSQUIRRELVM vm, const SQChar *s_,...);
235 
236 	/// set error message, used in errorhandlers
set_error(const char * error)237 	void set_error(const char* error) { error_msg = error; }
238 
239 	/// custom print handler
240 	static void printfunc(HSQUIRRELVM, const SQChar *s, ...);
241 
242 	/// path to files to #include
243 	plainstring include_path;
244 };
245 
246 /**
247  * Class to manage all vm's that are suspended and waiting for the return of
248  * a call to a tool.
249  */
250 class suspended_scripts_t {
251 private:
252 	static inthashtable_tpl<uint32,HSQUIRRELVM> suspended_scripts;
253 
254 	static HSQUIRRELVM remove_suspended_script(uint32 key);
255 
256 public:
257 	// generates key from a pointer
258 	static uint32 get_unique_key(void* key);
259 
260 	static void register_suspended_script(uint32 key, HSQUIRRELVM vm);
261 
262 	// remove any reference to given vm
263 	static void remove_vm(HSQUIRRELVM vm);
264 
265 	template<class R>
tell_return_value(uint32 key,R & ret)266 	static void tell_return_value(uint32 key, R& ret)
267 	{
268 		HSQUIRRELVM vm = remove_suspended_script(key);
269 		if (vm) {
270 			script_api::param<R>::push(vm, ret);
271 			sq_setwakeupretvalue(vm);
272 			// this vm can be woken up now
273 			sq_pushregistrytable(vm);
274 			bool wait = false;
275 			script_api::create_slot(vm, "wait_external", wait);
276 			sq_poptop(vm);
277 		}
278 	}
279 };
280 
281 #endif
282