- Avoid some expensive SDL_GetTicks() calls
[supertux.git] / src / scripting / squirrel_util.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 #include <config.h>
20
21 #include <stdexcept>
22 #include <sstream>
23 #include <stdarg.h>
24 #include <squirrel.h>
25 #include <sqstdmath.h>
26 #include <sqstdblob.h>
27 #include <sqstdstring.h>
28 #include <sqstdaux.h>
29 #include <sqstdio.h>
30 #include "squirrel_util.hpp"
31 #include "log.hpp"
32 #include "level.hpp"
33 #include "physfs/physfs_stream.hpp"
34
35 #ifdef ENABLE_SQDBG
36 #include <sqdbg/sqrdbg.h>
37
38 static HSQREMOTEDBG debugger = NULL;
39 #endif
40
41 namespace Scripting
42 {
43
44 HSQUIRRELVM global_vm = NULL;
45
46 static void printfunc(HSQUIRRELVM, const char* str, ...)
47 {
48   char buf[4096];
49   va_list arglist;
50   va_start(arglist, str);
51   vsprintf(buf, str, arglist);
52   Console::output << (const char*) buf << std::flush;
53   va_end(arglist);
54 }
55
56 void init_squirrel(bool enable_debugger)
57 {
58   global_vm = sq_open(64);
59   if(global_vm == NULL)
60     throw std::runtime_error("Couldn't initialize squirrel vm");
61
62 #ifdef ENABLE_SQDBG
63   if(enable_debugger) {
64     sq_enabledebuginfo(global_vm, SQTrue);
65     debugger = sq_rdbg_init(global_vm, 1234, SQFalse);
66     if(debugger == NULL)
67       throw SquirrelError(global_vm, "Couldn't initialize squirrel debugger");
68   
69     sq_enabledebuginfo(global_vm, SQTrue);
70     log_info << "Waiting for debug client..." << std::endl;
71     if(SQ_FAILED(sq_rdbg_waitforconnections(debugger)))
72       throw SquirrelError(global_vm, "Waiting for debug clients failed");
73     log_info << "debug client connected." << std::endl;
74   }
75 #endif
76
77   sq_pushroottable(global_vm);
78   if(sqstd_register_bloblib(global_vm) < 0)
79     throw SquirrelError(global_vm, "Couldn't register blob lib");
80   if(sqstd_register_mathlib(global_vm) < 0)
81     throw SquirrelError(global_vm, "Couldn't register math lib");
82   if(sqstd_register_stringlib(global_vm) < 0)
83     throw SquirrelError(global_vm, "Couldn't register string lib");
84   // register supertux API
85   register_supertux_wrapper(global_vm);
86
87   // TODO remove this at some point... it shoud just be functions not an object
88   expose_object(global_vm, -1, new Scripting::Level(), "Level", true);
89   
90   sq_pop(global_vm, 1);
91
92   // register print function
93   sq_setprintfunc(global_vm, printfunc);
94   // register default error handlers
95   sqstd_seterrorhandlers(global_vm);
96
97   // try to load default script
98   try {
99     std::string filename = "scripts/default.nut";
100     IFileStream stream(filename);
101     Scripting::compile_and_run(global_vm, stream, filename);
102   } catch(std::exception& e) {
103     log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
104   }
105 }
106
107 void exit_squirrel()
108 {
109 #ifdef ENABLE_SQDBG
110   if(debugger != NULL) {
111     sq_rdbg_shutdown(debugger);
112     debugger = NULL;
113   }
114 #endif
115   sq_close(global_vm);
116   global_vm = NULL;
117 }
118
119 void update_debugger()
120 {
121 #ifdef ENABLE_SQDBG
122   if(debugger != NULL)
123     sq_rdbg_update(debugger);
124 #endif
125 }
126
127 std::string squirrel2string(HSQUIRRELVM v, int i)
128 {
129   std::ostringstream os;
130   switch(sq_gettype(v, i))
131     {
132     case OT_NULL:
133       os << "<null>";        
134       break;
135     case OT_BOOL: {
136       SQBool p;
137       sq_getbool(v, i, &p);
138       if (p) 
139         os << "true";
140       else
141         os << "false";
142       break;
143     }
144     case OT_INTEGER: {
145       int val;
146       sq_getinteger(v, i, &val);
147       os << val;
148       break;
149     }
150     case OT_FLOAT: {
151       float val;
152       sq_getfloat(v, i, &val);
153       os << val;
154       break;
155     }
156     case OT_STRING: {
157       const char* val;
158       sq_getstring(v, i, &val);
159       os << "\"" << val << "\"";
160       break;    
161     }
162     case OT_TABLE: {
163       bool first = true;
164       os << "{";
165       sq_pushnull(v);  //null iterator
166       while(SQ_SUCCEEDED(sq_next(v,i-1)))
167         {
168           if (!first) {
169             os << ", ";
170           }
171           first = false;
172
173           //here -1 is the value and -2 is the key
174           os << squirrel2string(v, -2) << " => " 
175              << squirrel2string(v, -1);
176                               
177           sq_pop(v,2); //pops key and val before the nex iteration
178         }
179       sq_pop(v, 1);
180       os << "}";
181       break;
182     }
183     case OT_ARRAY: {
184       bool first = true;
185       os << "[";
186       sq_pushnull(v);  //null iterator
187       while(SQ_SUCCEEDED(sq_next(v,i-1)))
188         {
189           if (!first) {
190             os << ", ";
191           }
192           first = false;
193
194           //here -1 is the value and -2 is the key
195           // we ignore the key, since that is just the index in an array
196           os << squirrel2string(v, -1);
197                               
198           sq_pop(v,2); //pops key and val before the nex iteration
199         }
200       sq_pop(v, 1);
201       os << "]";
202       break;
203     }
204     case OT_USERDATA:
205       os << "<userdata>";
206       break;
207     case OT_CLOSURE:        
208       os << "<closure>";
209       break;
210     case OT_NATIVECLOSURE:
211       os << "<native closure>";
212       break;
213     case OT_GENERATOR:
214       os << "<generator>";
215       break;
216     case OT_USERPOINTER:
217       os << "userpointer";
218       break;
219     case OT_THREAD:
220       os << "<thread>";
221       break;
222     case OT_CLASS:
223       os << "<class>";
224       break;
225     case OT_INSTANCE:
226       os << "<instance>";
227       break;
228     case OT_WEAKREF:
229       os << "<weakref>";
230       break;
231     default:
232       os << "<unknown>";
233       break;
234     }
235   return os.str();
236 }
237
238 void print_squirrel_stack(HSQUIRRELVM v)
239 {
240     printf("--------------------------------------------------------------\n");
241     int count = sq_gettop(v);
242     for(int i = 1; i <= count; ++i) {
243         printf("%d: ",i);
244         switch(sq_gettype(v, i))
245         {
246             case OT_NULL:
247                 printf("null");        
248                 break;
249             case OT_INTEGER: {
250                 int val;
251                 sq_getinteger(v, i, &val);
252                 printf("integer (%d)", val);
253                 break;
254             }
255             case OT_FLOAT: {
256                 float val;
257                 sq_getfloat(v, i, &val);
258                 printf("float (%f)", val);
259                 break;
260             }
261             case OT_STRING: {
262                 const char* val;
263                 sq_getstring(v, i, &val);
264                 printf("string (%s)", val);
265                 break;    
266             }
267             case OT_TABLE:
268                 printf("table");
269                 break;
270             case OT_ARRAY:
271                 printf("array");
272                 break;
273             case OT_USERDATA:
274                 printf("userdata");
275                 break;
276             case OT_CLOSURE:        
277                 printf("closure(function)");    
278                 break;
279             case OT_NATIVECLOSURE:
280                 printf("native closure(C function)");
281                 break;
282             case OT_GENERATOR:
283                 printf("generator");
284                 break;
285             case OT_USERPOINTER:
286                 printf("userpointer");
287                 break;
288             case OT_THREAD:
289                 printf("thread");
290                 break;
291             case OT_CLASS:
292                 printf("class");
293                 break;
294             case OT_INSTANCE:
295                 printf("instance");
296                 break;
297             case OT_WEAKREF:
298                 printf("weakref");
299                 break;
300             default:
301                 printf("unknown?!?");
302                 break;
303         }
304         printf("\n");
305     }
306     printf("--------------------------------------------------------------\n");
307 }
308
309 static SQInteger squirrel_read_char(SQUserPointer file)
310 {
311   std::istream* in = reinterpret_cast<std::istream*> (file);
312   char c = in->get();
313   if(in->eof())
314     return 0;
315   return c;
316 }
317
318 void compile_script(HSQUIRRELVM vm, std::istream& in, const std::string& sourcename)
319 {
320   if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in, sourcename.c_str(), true)))
321     throw SquirrelError(vm, "Couldn't parse script");  
322 }
323
324 void compile_and_run(HSQUIRRELVM vm, std::istream& in,
325                      const std::string& sourcename)
326 {
327   compile_script(vm, in, sourcename);
328   
329   int oldtop = sq_gettop(vm);
330
331   try {
332     sq_pushroottable(vm);
333     if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
334       throw SquirrelError(vm, "Couldn't start script");
335   } catch(...) {
336     sq_settop(vm, oldtop);
337     throw;
338   }
339
340   // we can remove the closure in case the script was not suspended
341   if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
342     sq_settop(vm, oldtop-1);
343   }
344 }
345
346 HSQOBJECT create_thread(HSQUIRRELVM vm)
347 {
348   HSQUIRRELVM new_vm = sq_newthread(vm, 64);
349   if(new_vm == NULL)
350     throw SquirrelError(vm, "Couldn't create new VM");
351
352   HSQOBJECT vm_object;
353   sq_resetobject(&vm_object);
354   if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
355     throw SquirrelError(vm, "Couldn't get squirrel thread from stack");
356   sq_addref(vm, &vm_object);
357   
358   sq_pop(vm, 1);
359
360   return vm_object;
361 }
362
363 HSQOBJECT vm_to_object(HSQUIRRELVM vm)
364 {
365   HSQOBJECT object;
366   sq_resetobject(&object);
367   object._unVal.pThread = vm;
368   object._type = OT_THREAD;
369
370   return object;
371 }
372
373 HSQUIRRELVM object_to_vm(HSQOBJECT object)
374 {
375   if(object._type != OT_THREAD)
376     return NULL;
377
378   return object._unVal.pThread;
379 }
380
381 }