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