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