1 #include "script.h"
2
3 #include <stdarg.h>
4 #include <string.h>
5 #include "../squirrel/sqstdaux.h" // for error handlers
6 #include "../squirrel/sqstdio.h" // for loadfile
7 #include "../squirrel/sqstdstring.h" // export for scripts
8 #include "../squirrel/sqstdmath.h" // export for scripts
9 #include "../squirrel/sqstdsystem.h" // export for scripts
10 #include "../squirrel/sq_extensions.h" // for sq_call_restricted
11
12 #include "../utils/log.h"
13
14 #include "../tpl/inthashtable_tpl.h"
15 #include "../tpl/vector_tpl.h"
16 // for error popups
17 #include "../gui/help_frame.h"
18 #include "../gui/simwin.h"
19 #include "../utils/cbuffer_t.h"
20 #include "../utils/plainstring.h"
21
22
23 // debug: store stack pointer
24 #define BEGIN_STACK_WATCH(v) int stack_top = sq_gettop(v);
25 // debug: compare stack pointer with expected stack pointer, raise warning if failure
26 #define END_STACK_WATCH(v, delta) if ( (stack_top+(delta)) != sq_gettop(v)) { dbg->warning( __FUNCTION__, "(%d) stack in %d expected %d out %d", __LINE__,stack_top,stack_top+(delta),sq_gettop(v)); }
27
28
printfunc(HSQUIRRELVM vm,const SQChar * s,...)29 void script_vm_t::printfunc(HSQUIRRELVM vm, const SQChar *s, ...)
30 {
31 va_list vl;
32 va_start(vl, s);
33 if (script_vm_t *script = (script_vm_t*)sq_getforeignptr(vm)) {
34 script->log->vmessage("Script", "Print", s, vl);
35 }
36 va_end(vl);
37 }
38
39
errorfunc(HSQUIRRELVM vm,const SQChar * s_,...)40 void script_vm_t::errorfunc(HSQUIRRELVM vm, const SQChar *s_, ...)
41 {
42
43 char *s = strdup(s_);
44 char *s_dup = s;
45 // remove these linebreaks
46 while (s && *s=='\n') s++;
47 while (s && s[strlen(s)-1]=='\n') s[strlen(s)-1]=0;
48
49 // get failed script
50 script_vm_t *script = (script_vm_t*)sq_getforeignptr(vm);
51
52 if (script) {
53 va_list vl;
54 va_start(vl, s_);
55 script->log->vmessage("Script", "Error", s, vl);
56 va_end(vl);
57 }
58
59 // only one error message at a time - hopefully
60 // collect into static buffer
61 static cbuffer_t buf;
62 if (strcmp(s, "<error>")==0) {
63 // start of error message
64 buf.clear();
65 buf.printf("<st>An error occured within a script!</st><br>\n");
66 }
67 else if (strcmp(s, "</error>")==0) {
68 // end of error message
69 help_frame_t *win = dynamic_cast<help_frame_t*>(win_get_magic((ptrdiff_t)script));
70 if (win == NULL) {
71 win = new help_frame_t();
72 create_win( win, w_info, magic_script_error);
73 }
74 win->set_text(buf);
75 win->set_name("Script error occurred");
76
77 if (script) {
78 script->set_error(buf);
79 }
80 }
81 else {
82 va_list vl;
83 va_start(vl, s_);
84 buf.vprintf(s, vl);
85 va_end(vl);
86 buf.append("<br>");
87 }
88 free(s_dup);
89 }
90
91 void export_include(HSQUIRRELVM vm, const char* include_path); // api_include.cc
92
93 // virtual machine
script_vm_t(const char * include_path_,const char * log_name)94 script_vm_t::script_vm_t(const char* include_path_, const char* log_name)
95 {
96 vm = sq_open(1024);
97 sqstd_seterrorhandlers(vm);
98 sq_setprintfunc(vm, printfunc, errorfunc);
99 register_vm(vm);
100 log = new log_t(log_name, true, true, true, "script engine started.\n");
101
102 // store ptr to us in vm
103 sq_setforeignptr(vm, this);
104
105 // create thread, and put it into registry-table
106 sq_pushregistrytable(vm);
107 sq_pushstring(vm, "thread", -1);
108 thread = sq_newthread(vm, 100);
109 sq_newslot(vm, -3, false);
110 register_vm(thread);
111 // create queue array
112 sq_pushstring(vm, "queue", -1);
113 sq_newarray(vm, 0);
114 sq_newslot(vm, -3, false);
115 // create queued-callbacks array
116 sq_pushstring(vm, "queued_callbacks", -1);
117 sq_newarray(vm, 0);
118 sq_newslot(vm, -3, false);
119 // pop registry
120 sq_pop(vm, 1);
121 // store ptr to us in vm
122 sq_setforeignptr(vm, this);
123 sq_setforeignptr(thread, this);
124
125 error_msg = NULL;
126 include_path = include_path_;
127 // register libraries
128 sq_pushroottable(vm);
129 sqstd_register_stringlib(vm);
130 sqstd_register_mathlib(vm);
131 sqstd_register_systemlib(vm);
132 sq_pop(vm, 1);
133 // export include command
134 export_include(vm, include_path);
135 // initialize coordinate and rotation handling
136 script_api::coordinate_transform_t::initialize();
137 }
138
139
~script_vm_t()140 script_vm_t::~script_vm_t()
141 {
142 unregister_vm(thread);
143 unregister_vm(vm);
144 // remove from suspended calls list
145 suspended_scripts_t::remove_vm(thread);
146 suspended_scripts_t::remove_vm(vm);
147 // close vm, also closes thread
148 sq_close(vm);
149
150 delete log;
151 }
152
call_script(const char * filename)153 const char* script_vm_t::call_script(const char* filename)
154 {
155 // load script
156 if (!SQ_SUCCEEDED(sqstd_loadfile(vm, filename, true))) {
157 return "Reading / compiling script failed";
158 }
159 // call it
160 sq_pushroottable(vm);
161 if (!SQ_SUCCEEDED(sq_call_restricted(vm, 1, SQFalse, SQTrue, 100000))) {
162 sq_pop(vm, 1); // pop script
163 return "Call script failed";
164 }
165 if (sq_getvmstate(vm) == SQ_VMSTATE_SUSPENDED) {
166 set_error("Calling scriptfile took too long");
167 return get_error();
168 }
169 sq_pop(vm, 1); // pop script
170 return NULL;
171 }
172
173
eval_string(const char * squirrel_string)174 const char* script_vm_t::eval_string(const char* squirrel_string)
175 {
176 // compile string
177 if (!SQ_SUCCEEDED(sq_compilebuffer(vm, squirrel_string, strlen(squirrel_string), "userdefinedstringmethod", true))) {
178 set_error("Error compiling string buffer");
179 return get_error();
180 }
181 // execute
182 sq_pushroottable(vm);
183 sq_call_restricted(vm, 1, SQFalse, SQTrue, 100000);
184 sq_pop(vm, 1);
185 return get_error();
186 }
187
188
is_call_suspended(const char * err)189 bool script_vm_t::is_call_suspended(const char* err)
190 {
191 return (err != NULL) && ( strcmp(err, "suspended") == 0);
192 }
193
194
intern_prepare_call(HSQUIRRELVM & job,call_type_t ct,const char * function)195 const char* script_vm_t::intern_prepare_call(HSQUIRRELVM &job, call_type_t ct, const char* function)
196 {
197 const char* err = NULL;
198
199 switch (ct) {
200 case FORCE:
201 case FORCEX:
202 job = vm;
203 // block calls to suspendable functions
204 sq_block_suspend(job, function);
205 break;
206 case TRY:
207 if (sq_getvmstate(thread) == SQ_VMSTATE_SUSPENDED) {
208 return "suspended";
209 }
210 // fall through
211 case QUEUE:
212 job = thread;
213 break;
214 }
215
216 dbg->message("script_vm_t::intern_prepare_call", "ct=%d function=%s stack=%d", ct, function, sq_gettop(job));
217 sq_pushroottable(job);
218 sq_pushstring(job, function, -1);
219 // fetch function from root
220 if (SQ_SUCCEEDED(sq_get(job,-2))) {
221 sq_remove(job, -2); // root table
222 sq_pushroottable(job);
223 }
224 else {
225 err = "Function not found";
226 sq_poptop(job); // array, root table
227 }
228 return err;
229 // stack: closure, root table (1st param)
230 }
231
232
intern_finish_call(HSQUIRRELVM job,call_type_t ct,int nparams,bool retvalue)233 const char* script_vm_t::intern_finish_call(HSQUIRRELVM job, call_type_t ct, int nparams, bool retvalue)
234 {
235 BEGIN_STACK_WATCH(job);
236 // stack: closure, nparams*objects
237 const char* err = NULL;
238 bool suspended = sq_getvmstate(job) == SQ_VMSTATE_SUSPENDED;
239 // check queue, if not empty resume first job in queue
240 if (!suspended && ct != FORCE && ct != FORCEX) {
241 sq_pushregistrytable(job);
242 sq_pushstring(job, "queue", -1);
243 sq_get(job, -2);
244 // stack: registry, queue
245 if (sq_getsize(job, -1) > 0) {
246 suspended = true;
247 }
248 sq_pop(job, 2);
249 }
250 // queue function call?
251 if (suspended && ct == QUEUE) {
252 intern_queue_call(job, nparams, retvalue);
253 err = "suspended";
254 // stack: clean
255 }
256 if (suspended) {
257 intern_resume_call(job);
258 }
259 if (!suspended || ct == FORCE || ct == FORCEX) {
260 // set active callback if call could be suspended
261 if (ct == QUEUE) {
262 intern_make_pending_callback_active();
263 }
264 // mark as not queued
265 sq_pushregistrytable(job);
266 script_api::create_slot(job, "was_queued", false);
267 sq_poptop(job);
268 END_STACK_WATCH(job,0);
269 err = intern_call_function(job, ct, nparams, retvalue);
270 }
271 return err;
272 }
273
274 /**
275 * Calls function. If it was a queued call, also calls callbacks.
276 * Stack(job): expects closure, nparam*objects, on exit: return value (or clean stack if failure).
277 * @returns NULL on success, error message or "suspended" otherwise.
278 */
intern_call_function(HSQUIRRELVM job,call_type_t ct,int nparams,bool retvalue)279 const char* script_vm_t::intern_call_function(HSQUIRRELVM job, call_type_t ct, int nparams, bool retvalue)
280 {
281 BEGIN_STACK_WATCH(job);
282 dbg->message("script_vm_t::intern_call_function", "start: stack=%d nparams=%d ret=%d", sq_gettop(job), nparams, retvalue);
283 const char* err = NULL;
284 uint32 opcodes = ct == FORCEX ? 100000 : 10000;
285 // call the script
286 if (!SQ_SUCCEEDED(sq_call_restricted(job, nparams, retvalue, ct == FORCE || ct == FORCEX, opcodes))) {
287 err = "Call function failed";
288 retvalue = false;
289 }
290 if (ct == FORCE || ct == FORCEX) {
291 sq_block_suspend(job, NULL);
292 }
293 if (sq_getvmstate(job) != SQ_VMSTATE_SUSPENDED) {
294 // remove closure
295 sq_remove(job, retvalue ? -2 : -1);
296 if (ct == QUEUE && retvalue) {
297
298 sq_pushregistrytable(job);
299 // notify call backs only if call was queued
300 bool notify = false;
301 script_api::get_slot(job, "was_queued", notify, -1);
302 sq_pop(job, 1);
303
304 if (notify) {
305 intern_call_callbacks(job);
306 }
307 }
308 END_STACK_WATCH(job, -nparams-1 + retvalue);
309 }
310 else {
311 assert(ct != FORCE && ct != FORCEX);
312 // call suspended: pop dummy return value
313 if (retvalue) {
314 sq_poptop(job);
315 }
316 // save retvalue flag, number of parameters
317 sq_pushregistrytable(job);
318 script_api::create_slot(job, "retvalue", retvalue);
319 script_api::create_slot(job, "nparams", nparams);
320 sq_poptop(job);
321 err = "suspended";
322 }
323 dbg->message("script_vm_t::intern_call_function", "stack=%d err=%s", sq_gettop(job)-retvalue, err);
324 return err;
325 }
326
intern_resume_call(HSQUIRRELVM job)327 void script_vm_t::intern_resume_call(HSQUIRRELVM job)
328 {
329 BEGIN_STACK_WATCH(job);
330 // stack: clean
331 // get retvalue flag
332 sq_pushregistrytable(job);
333 bool retvalue = false;
334 script_api::get_slot(job, "retvalue", retvalue, -1);
335 int nparams = 0;
336 script_api::get_slot(job, "nparams", nparams, -1);
337 bool wait = false;
338 script_api::get_slot(job, "wait_external", wait, -1);
339 sq_pop(job, 1);
340 END_STACK_WATCH(job, 0);
341
342 if (wait) {
343 dbg->message("script_vm_t::intern_resume_call", "waits for external call to be able to proceed");
344 return;
345 }
346 if (!sq_canresumevm(job)) {
347 // vm waits for return value to suspended call
348 dbg->message("script_vm_t::intern_resume_call", "waiting for return value");
349 return;
350 }
351 // vm suspended, but not from call to our methods
352 if (nparams < 0) {
353 retvalue = false;
354 }
355
356 // resume v.m.
357 if (!SQ_SUCCEEDED(sq_resumevm(job, retvalue, 10000))) {
358 retvalue = false;
359 }
360 // if finished, clear stack
361 if (sq_getvmstate(job) != SQ_VMSTATE_SUSPENDED) {
362
363 if (nparams >=0 ) {
364 BEGIN_STACK_WATCH(job);
365 // remove closure & args
366 for(int i=0; i<nparams+1; i++) {
367 sq_remove(job, retvalue ? -2 : -1);
368 }
369 if (retvalue) {
370 intern_call_callbacks(job);
371 sq_poptop(job);
372 }
373 dbg->message("script_vm_t::intern_resume_call", "in between stack=%d", sq_gettop(job));
374 END_STACK_WATCH(job, -1-nparams-retvalue);
375 }
376 else {
377 // brute force clean stack
378 sq_settop(job, 0);
379 }
380
381 // make nparams invalid
382 sq_pushregistrytable(job);
383 script_api::create_slot(job, "nparams", -1);
384 sq_poptop(job);
385
386 // proceed with next call in queue
387 if (intern_prepare_queued(job, nparams, retvalue)) {
388 const char* err = intern_call_function(job, QUEUE, nparams, retvalue);
389 if (err == NULL && retvalue) {
390 // remove return value: call was queued thus remove return value from stack
391 sq_poptop(job);
392 }
393 }
394 }
395 else {
396 if (retvalue) {
397 sq_poptop(job);
398 }
399 }
400
401 dbg->message("script_vm_t::intern_resume_call", "stack=%d", sq_gettop(job));
402 }
403
404 /**
405 * Stack(job): expects closure, nparams*objects, clean on exit.
406 * Put call into registry.queue, callback into registry.queued_callbacks.
407 * Cleans registry.pending_callback.
408 * @param nparams number of parameter of the queued call
409 * @param retvalue whether the call would return something
410 */
intern_queue_call(HSQUIRRELVM job,int nparams,bool retvalue)411 void script_vm_t::intern_queue_call(HSQUIRRELVM job, int nparams, bool retvalue)
412 {
413 BEGIN_STACK_WATCH(job);
414 // stack: closure, nparams*objects
415 // queue call: put closure and parameters in array
416 script_api::param<bool>::push(job, retvalue);
417 sq_newarray(job, 0); // to hold parameters for queued calls
418 // stack: closure, nparams*objects, retvalue, array
419 for(int i=nparams+2; i>0; i--) {
420 sq_push(job, -2);
421 sq_arrayappend(job, -2);
422 sq_remove(job, -2);
423 }
424 // stack: array
425 sq_pushregistrytable(job);
426 sq_pushstring(job, "queue", -1);
427 sq_get(job, -2);
428 // stack: array, registry, queue
429 // search queue whether our call is already there
430 sint32 size = sq_getsize(job, -1);
431 bool equal = false;
432 for(sint32 i=0; (i<size) && !equal; i++) {
433 sq_pushinteger(job, i);
434 sq_get(job, -2);
435 // stack: [...], queue[i]
436 sint32 n = sq_getsize(job, -1);
437 if (n != nparams+2) {
438 continue; // different number of arguments
439 }
440 equal = true;
441 // compare arguments of call (including closure and retvalue)
442 for(sint32 j=0; (j<n) && equal; j++) {
443 sq_pushinteger(job, j);
444 sq_get(job, -2);
445 // stack: array, registry, queue, queue[i], queue[i][j]
446 sq_pushinteger(job, j);
447 sq_get(job, -6);
448 equal = sq_cmp(job)==0;
449 sq_pop(job, 2);
450 // stack: array, registry, queue, queue[i]
451 }
452 // found identic call
453 // add callback to queue
454 if (equal) {
455 sq_pushstring(job, "queued_callbacks", -1);
456 sq_get(job, -4);
457 sq_pushinteger(job, i);
458 sq_get(job, -2);
459 // stack: array, registry, queue, queue[i], queued_callbacks, queued_callbacks[i]
460 sq_pushstring(job, "pending_callback", -1);
461 // delete pending_callback slot and push it
462 if (SQ_SUCCEEDED( sq_deleteslot(job, -6, true) )) {
463 sq_arrayappend(job, -2);
464 }
465 sq_pop(job, 2);
466 }
467 sq_poptop(job);
468 }
469 // stack: array, registry, queue
470 if (!equal) {
471 sq_push(job, -3);
472 sq_arrayappend(job, -2);
473 // add callback to queue
474 sq_pushstring(job, "queued_callbacks", -1);
475 sq_get(job, -3);
476 sq_newarray(job, 10);
477 // stack: array, registry, queue, queued_callbacks, queued_callbacks[end]
478 sq_pushstring(job, "pending_callback", -1);
479 // delete pending_callback slot and push it
480 if (SQ_SUCCEEDED( sq_deleteslot(job, -5, true) )) {
481 sq_arrayappend(job, -2);
482 }
483 sq_arrayappend(job, -2);
484 sq_poptop(job);
485 }
486 else {
487 dbg->message("script_vm_t::intern_queue_call", "NOT QUEUED stack=%d", sq_gettop(job));
488 }
489 sq_pop(job, 3);
490
491 END_STACK_WATCH(job, -nparams-1);
492 dbg->message("script_vm_t::intern_queue_call", "stack=%d", sq_gettop(job));
493 // stack: clean
494 }
495
496 /**
497 * Removes queue(0) and puts it on stack.
498 * Activates callback by intern_make_queued_callback_active().
499 * Stack(job): on success - closure, nparams*objects, on failure - unchanged.
500 * @param nparams number of parameters of the call on the stack
501 * @param retvalue whether queued call returns something
502 * @returns true if a new queued call is on the stack
503 */
intern_prepare_queued(HSQUIRRELVM job,int & nparams,bool & retvalue)504 bool script_vm_t::intern_prepare_queued(HSQUIRRELVM job, int &nparams, bool &retvalue)
505 {
506 BEGIN_STACK_WATCH(job);
507 // queued calls
508 sq_pushregistrytable(job);
509 sq_pushstring(job, "queue", -1);
510 sq_get(job, -2);
511 sq_pushinteger(job, 0);
512 if (SQ_SUCCEEDED(sq_get(job, -2))) {
513 // there are suspended jobs
514 // stack: registry, queue, array[ retvalue nparams*objects closure]
515 nparams = -2;
516 while( SQ_SUCCEEDED(sq_arraypop(job, -3-nparams, true))) {
517 nparams++;
518 }
519 retvalue = script_api::param<bool>::get(job, -1);
520 sq_poptop(job);
521
522 intern_make_queued_callback_active();
523 // stack: registry, queue, array, closure, nparams*objects
524 END_STACK_WATCH(job, nparams+4);
525 // mark this call as queued
526 sq_pushstring(job, "was_queued", -1);
527 script_api::param<bool>::push(job, true);
528 sq_createslot(job, -6-nparams);
529 // cleanup
530 sq_remove(job, -2-nparams);
531 sq_arrayremove(job, -2-nparams, 0);
532 sq_remove(job, -2-nparams);
533 sq_remove(job, -2-nparams);
534 // stack: closure, nparams*objects
535 dbg->message("script_vm_t::intern_prepare_queued", "stack=%d nparams=%d ret=%d", sq_gettop(job), nparams, retvalue);
536
537 END_STACK_WATCH(job, nparams+1);
538 return true;
539 }
540 else {
541 // no suspended jobs: clean stack
542 sq_pop(job, 2);
543 }
544 END_STACK_WATCH(job, 0);
545 return false;
546 }
547
548
549 /**
550 * Prepare function call.
551 * Stack(vm): pushes registry, nret, closure, root.
552 */
intern_prepare_pending_callback(const char * function,sint32 nret)553 bool script_vm_t::intern_prepare_pending_callback(const char* function, sint32 nret)
554 {
555 BEGIN_STACK_WATCH(vm);
556 sq_pushregistrytable(vm);
557 script_api::param<sint32>::push(vm, nret+1); // +1 to account for root table
558 sq_pushstring(vm, function, -1);
559 bool ok = SQ_SUCCEEDED(sq_get(vm, -3));
560 if (ok) {
561 // stack: registry, nret, closure, root
562 sq_pushroottable(vm);
563 }
564 else {
565 // stack: clear
566 sq_pop(vm, 2);
567 }
568 END_STACK_WATCH(vm, ok ? 4 : 0);
569 return ok;
570 }
571
572 /**
573 * Sets registry.pending_callback.
574 * Stack(vm): expects registry, nret, closure, nparams*objects; clears upon exit.
575 */
intern_store_pending_callback(sint32 nparams)576 void script_vm_t::intern_store_pending_callback(sint32 nparams)
577 {
578 BEGIN_STACK_WATCH(vm);
579 // stack: registry, nret, closure, nparams*objects
580 // put closure and parameters in array
581 sq_newarray(vm, 0);
582 // stack: registry, nret, closure, nparams*objects, array
583 for(int i=0; i<nparams+2; i++) {
584 sq_push(vm, -2);
585 sq_arrayappend(vm, -2);
586 sq_remove(vm, -2);
587 }
588 // stack: registry, array
589 sq_pushstring(vm, "pending_callback", -1);
590 sq_push(vm, -2);
591 // stack: registry, array, string, array
592 sq_createslot(vm, -4);
593 sq_pop(vm, 2);
594 END_STACK_WATCH(vm,-nparams-3);
595 }
596
597 /**
598 * Deletes registry.pending_callback.
599 * Stack(vm): unchanged
600 */
clear_pending_callback()601 void script_vm_t::clear_pending_callback()
602 {
603 BEGIN_STACK_WATCH(vm);
604
605 sq_pushregistrytable(vm);
606 sq_pushstring(vm, "pending_callback", -1);
607 sq_deleteslot(vm, -2, false);
608 sq_poptop(vm);
609 END_STACK_WATCH(vm,0);
610 }
611
612 /**
613 * Sets registry.active_callbacks.
614 * Deletes registry.queued_callbacks[0].
615 * Stack(vm) unchanged.
616 */
intern_make_queued_callback_active()617 void script_vm_t::intern_make_queued_callback_active()
618 {
619 BEGIN_STACK_WATCH(vm);
620 sq_pushregistrytable(vm);
621 sq_pushstring(vm, "queued_callbacks", -1);
622 sq_get(vm, -2);
623 sq_pushstring(vm, "active_callbacks", -1);
624 sq_pushinteger(vm, 0);
625 if (SQ_SUCCEEDED(sq_get(vm, -3))) {
626 // stack: registry, queued_callbacks, "..", queued_callbacks[0]
627 // active_callbacks = queued_callbacks[0]
628 sq_createslot(vm, -4);
629 // remove queued_callbacks[0]
630 sq_arrayremove(vm, -1, 0);
631 }
632 else {
633 sq_poptop(vm);
634 }
635 sq_pop(vm,2);
636 END_STACK_WATCH(vm,0);
637 }
638
639 /**
640 * Sets registry.active_callbacks.
641 * Deletes registry.pending_callback.
642 * Stack(vm) unchanged.
643 */
intern_make_pending_callback_active()644 void script_vm_t::intern_make_pending_callback_active()
645 {
646 BEGIN_STACK_WATCH(vm);
647 sq_pushregistrytable(vm);
648 sq_pushstring(vm, "active_callbacks", -1);
649 sq_newarray(vm, 1);
650 sq_pushstring(vm, "pending_callback", -1);
651 if (SQ_SUCCEEDED( sq_deleteslot(vm, -4, true) ) ) {
652 // stack: registry, "..", array[], pending_callback
653 sq_arrayappend(vm, -2);
654 // stack: registry, "..", array[ 0=>pending_callback ]
655 sq_createslot(vm, -3);
656 }
657 else {
658 // stack: registry, "..", array[]
659 sq_poptop(vm);
660 // delete active_callbacks slot
661 sq_deleteslot(vm, -2, false);
662 }
663 sq_poptop(vm);
664 END_STACK_WATCH(vm,0);
665 }
666
667 /**
668 *
669 *
670 * Stack(job): return value, unchanged
671 */
intern_call_callbacks(HSQUIRRELVM job)672 void script_vm_t::intern_call_callbacks(HSQUIRRELVM job)
673 {
674 BEGIN_STACK_WATCH(job);
675 sq_pushregistrytable(job);
676 sq_pushstring(job, "active_callbacks", -1);
677 if (SQ_SUCCEEDED( sq_deleteslot(job, -2, true) ) ) {
678 BEGIN_STACK_WATCH(job);
679 if (SQ_SUCCEEDED( sq_arraypop(job, -1, true))) {
680 if (SQ_SUCCEEDED(sq_arraypop(job, -1, true))) {
681 sint32 nret = script_api::param<sint32>::get(job, -1);
682 sq_poptop(job);
683 BEGIN_STACK_WATCH(job);
684 // stack: retval, registry, active_cb, active_cb[end]=array[ nparams*objects closure]
685 int nparams = -1;
686 while( SQ_SUCCEEDED(sq_arraypop(job, -2-nparams, true))) {
687 nparams++;
688 // replace parameter by return value
689 if (nret > 0 && nret == nparams) {
690 sq_poptop(job);
691 sq_push(job, -4-nparams);
692 }
693 }
694 // stack: retval, registry, active_cb, active_cb[end], closure, nparams*objects
695 sq_call_restricted(job, nparams, false, true, 100);
696 // remove closure and finished callback
697 sq_pop(job, 1);
698 END_STACK_WATCH(job,0);
699 }
700 sq_poptop(job);
701 }
702 END_STACK_WATCH(job,0);
703 sq_poptop(job);
704 }
705 sq_poptop(job);
706 END_STACK_WATCH(job,0);
707 }
708
709
set_my_player(uint8 player_nr)710 void script_vm_t::set_my_player(uint8 player_nr)
711 {
712 sq_pushregistrytable(vm);
713 script_api::create_slot(vm, "my_player_nr", player_nr);
714 sq_poptop(vm);
715 }
716
717
718 /* -------- management of suspended scripts that wait for return value ----------- */
719
720 inthashtable_tpl<uint32,HSQUIRRELVM> suspended_scripts_t::suspended_scripts;
721
722
get_unique_key(void * ptr)723 uint32 suspended_scripts_t::get_unique_key(void* ptr)
724 {
725 uint32 key = (uint32)(size_t)ptr;
726 while (key == 0 || suspended_scripts.get(key)) {
727 key ++;
728 }
729 return key;
730 }
731
732
register_suspended_script(uint32 key,HSQUIRRELVM vm)733 void suspended_scripts_t::register_suspended_script(uint32 key, HSQUIRRELVM vm)
734 {
735 suspended_scripts.set(key, vm);
736
737 // mark vm to wait for external call to allow for wake-up
738 sq_pushregistrytable(vm);
739 bool wait = true;
740 script_api::create_slot(vm, "wait_external", wait);
741 sq_poptop(vm);
742 }
743
744
remove_suspended_script(uint32 key)745 HSQUIRRELVM suspended_scripts_t::remove_suspended_script(uint32 key)
746 {
747 return suspended_scripts.remove(key);
748 }
749
750
remove_vm(HSQUIRRELVM vm)751 void suspended_scripts_t::remove_vm(HSQUIRRELVM vm)
752 {
753 inthashtable_tpl<uint32,HSQUIRRELVM>::iterator iter=suspended_scripts.begin(), end=suspended_scripts.end();
754 for(; iter != end; ) {
755 if ( (*iter).value == vm) {
756 iter = suspended_scripts.erase(iter);
757 }
758 else {
759 ++iter;
760 }
761 }
762 }
763