src/scripting/squirrel_util.cpp

Go to the documentation of this file.
00001 //  SuperTux
00002 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
00003 //
00004 //  This program is free software: you can redistribute it and/or modify
00005 //  it under the terms of the GNU General Public License as published by
00006 //  the Free Software Foundation, either version 3 of the License, or
00007 //  (at your option) any later version.
00008 //
00009 //  This program is distributed in the hope that it will be useful,
00010 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 //  GNU General Public License for more details.
00013 //
00014 //  You should have received a copy of the GNU General Public License
00015 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 
00017 #include "scripting/squirrel_util.hpp"
00018 
00019 #include <config.h>
00020 
00021 #include <stdio.h>
00022 #include <sqstdaux.h>
00023 #include <sqstdblob.h>
00024 #include <sqstdmath.h>
00025 #include <sqstdstring.h>
00026 #include <stdarg.h>
00027 
00028 #include "physfs/ifile_stream.hpp"
00029 #include "supertux/console.hpp"
00030 #include "util/log.hpp"
00031 
00032 #ifdef ENABLE_SQDBG
00033 
00034 static HSQREMOTEDBG debugger = NULL;
00035 #endif
00036 
00037 namespace scripting {
00038 
00039 HSQUIRRELVM global_vm = NULL;
00040 
00041 static void printfunc(HSQUIRRELVM, const char* str, ...)
00042 {
00043   char buf[4096];
00044   va_list arglist;
00045   va_start(arglist, str);
00046   vsprintf(buf, str, arglist);
00047   Console::output << (const char*) buf << std::flush;
00048   va_end(arglist);
00049 }
00050 
00051 void init_squirrel(bool enable_debugger)
00052 {
00053   global_vm = sq_open(64);
00054   if(global_vm == NULL)
00055     throw std::runtime_error("Couldn't initialize squirrel vm");
00056 
00057   if(enable_debugger) {
00058 #ifdef ENABLE_SQDBG
00059     sq_enabledebuginfo(global_vm, SQTrue);
00060     debugger = sq_rdbg_init(global_vm, 1234, SQFalse);
00061     if(debugger == NULL)
00062       throw SquirrelError(global_vm, "Couldn't initialize squirrel debugger");
00063 
00064     sq_enabledebuginfo(global_vm, SQTrue);
00065     log_info << "Waiting for debug client..." << std::endl;
00066     if(SQ_FAILED(sq_rdbg_waitforconnections(debugger)))
00067       throw SquirrelError(global_vm, "Waiting for debug clients failed");
00068     log_info << "debug client connected." << std::endl;
00069 #endif
00070   }
00071 
00072   sq_pushroottable(global_vm);
00073   if(SQ_FAILED(sqstd_register_bloblib(global_vm)))
00074     throw SquirrelError(global_vm, "Couldn't register blob lib");
00075   if(SQ_FAILED(sqstd_register_mathlib(global_vm)))
00076     throw SquirrelError(global_vm, "Couldn't register math lib");
00077   if(SQ_FAILED(sqstd_register_stringlib(global_vm)))
00078     throw SquirrelError(global_vm, "Couldn't register string lib");
00079 
00080   // remove rand and srand calls from sqstdmath, we'll provide our own
00081   sq_pushstring(global_vm, "srand", -1);
00082   sq_deleteslot(global_vm, -2, SQFalse);
00083   sq_pushstring(global_vm, "rand", -1);
00084   sq_deleteslot(global_vm, -2, SQFalse);
00085 
00086   // register supertux API
00087   register_supertux_wrapper(global_vm);
00088 
00089   sq_pop(global_vm, 1);
00090 
00091   // register print function
00092   sq_setprintfunc(global_vm, printfunc);
00093   // register default error handlers
00094   sqstd_seterrorhandlers(global_vm);
00095 
00096   // try to load default script
00097   try {
00098     std::string filename = "scripts/default.nut";
00099     IFileStream stream(filename);
00100     scripting::compile_and_run(global_vm, stream, filename);
00101   } catch(std::exception& e) {
00102     log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
00103   }
00104 }
00105 
00106 void exit_squirrel()
00107 {
00108 #ifdef ENABLE_SQDBG
00109   if(debugger != NULL) {
00110     sq_rdbg_shutdown(debugger);
00111     debugger = NULL;
00112   }
00113 #endif
00114 
00115   if (global_vm)
00116     sq_close(global_vm);
00117 
00118   global_vm = NULL;
00119 }
00120 
00121 void update_debugger()
00122 {
00123 #ifdef ENABLE_SQDBG
00124   if(debugger != NULL)
00125     sq_rdbg_update(debugger);
00126 #endif
00127 }
00128 
00129 std::string squirrel2string(HSQUIRRELVM v, SQInteger i)
00130 {
00131   std::ostringstream os;
00132   switch(sq_gettype(v, i))
00133   {
00134     case OT_NULL:
00135       os << "<null>";
00136       break;
00137     case OT_BOOL: {
00138       SQBool p;
00139       sq_getbool(v, i, &p);
00140       if (p)
00141         os << "true";
00142       else
00143         os << "false";
00144       break;
00145     }
00146     case OT_INTEGER: {
00147       SQInteger val;
00148       sq_getinteger(v, i, &val);
00149       os << val;
00150       break;
00151     }
00152     case OT_FLOAT: {
00153       SQFloat val;
00154       sq_getfloat(v, i, &val);
00155       os << val;
00156       break;
00157     }
00158     case OT_STRING: {
00159       const SQChar* val;
00160       sq_getstring(v, i, &val);
00161       os << "\"" << val << "\"";
00162       break;
00163     }
00164     case OT_TABLE: {
00165       bool first = true;
00166       os << "{";
00167       sq_pushnull(v);  //null iterator
00168       while(SQ_SUCCEEDED(sq_next(v,i-1)))
00169       {
00170         if (!first) {
00171           os << ", ";
00172         }
00173         first = false;
00174 
00175         //here -1 is the value and -2 is the key
00176         os << squirrel2string(v, -2) << " => "
00177            << squirrel2string(v, -1);
00178 
00179         sq_pop(v,2); //pops key and val before the nex iteration
00180       }
00181       sq_pop(v, 1);
00182       os << "}";
00183       break;
00184     }
00185     case OT_ARRAY: {
00186       bool first = true;
00187       os << "[";
00188       sq_pushnull(v);  //null iterator
00189       while(SQ_SUCCEEDED(sq_next(v,i-1)))
00190       {
00191         if (!first) {
00192           os << ", ";
00193         }
00194         first = false;
00195 
00196         //here -1 is the value and -2 is the key
00197         // we ignore the key, since that is just the index in an array
00198         os << squirrel2string(v, -1);
00199 
00200         sq_pop(v,2); //pops key and val before the nex iteration
00201       }
00202       sq_pop(v, 1);
00203       os << "]";
00204       break;
00205     }
00206     case OT_USERDATA:
00207       os << "<userdata>";
00208       break;
00209     case OT_CLOSURE:
00210       os << "<closure>";
00211       break;
00212     case OT_NATIVECLOSURE:
00213       os << "<native closure>";
00214       break;
00215     case OT_GENERATOR:
00216       os << "<generator>";
00217       break;
00218     case OT_USERPOINTER:
00219       os << "userpointer";
00220       break;
00221     case OT_THREAD:
00222       os << "<thread>";
00223       break;
00224     case OT_CLASS:
00225       os << "<class>";
00226       break;
00227     case OT_INSTANCE:
00228       os << "<instance>";
00229       break;
00230     case OT_WEAKREF:
00231       os << "<weakref>";
00232       break;
00233     default:
00234       os << "<unknown>";
00235       break;
00236   }
00237   return os.str();
00238 }
00239 
00240 void print_squirrel_stack(HSQUIRRELVM v)
00241 {
00242   printf("--------------------------------------------------------------\n");
00243   int count = sq_gettop(v);
00244   for(int i = 1; i <= count; ++i) {
00245     printf("%d: ",i);
00246     switch(sq_gettype(v, i))
00247     {
00248       case OT_NULL:
00249         printf("null");
00250         break;
00251       case OT_INTEGER: {
00252         SQInteger val;
00253         sq_getinteger(v, i, &val);
00254         printf("integer (%d)", static_cast<int> (val));
00255         break;
00256       }
00257       case OT_FLOAT: {
00258         SQFloat val;
00259         sq_getfloat(v, i, &val);
00260         printf("float (%f)", val);
00261         break;
00262       }
00263       case OT_STRING: {
00264         const SQChar* val;
00265         sq_getstring(v, i, &val);
00266         printf("string (%s)", val);
00267         break;
00268       }
00269       case OT_TABLE:
00270         printf("table");
00271         break;
00272       case OT_ARRAY:
00273         printf("array");
00274         break;
00275       case OT_USERDATA:
00276         printf("userdata");
00277         break;
00278       case OT_CLOSURE:
00279         printf("closure(function)");
00280         break;
00281       case OT_NATIVECLOSURE:
00282         printf("native closure(C function)");
00283         break;
00284       case OT_GENERATOR:
00285         printf("generator");
00286         break;
00287       case OT_USERPOINTER:
00288         printf("userpointer");
00289         break;
00290       case OT_THREAD:
00291         printf("thread");
00292         break;
00293       case OT_CLASS:
00294         printf("class");
00295         break;
00296       case OT_INSTANCE:
00297         printf("instance");
00298         break;
00299       case OT_WEAKREF:
00300         printf("weakref");
00301         break;
00302       default:
00303         printf("unknown?!?");
00304         break;
00305     }
00306     printf("\n");
00307   }
00308   printf("--------------------------------------------------------------\n");
00309 }
00310 
00311 SQInteger squirrel_read_char(SQUserPointer file)
00312 {
00313   std::istream* in = reinterpret_cast<std::istream*> (file);
00314   char c = in->get();
00315   if(in->eof())
00316     return 0;
00317   return c;
00318 }
00319 
00320 void compile_script(HSQUIRRELVM vm, std::istream& in, const std::string& sourcename)
00321 {
00322   if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in, sourcename.c_str(), true)))
00323     throw SquirrelError(vm, "Couldn't parse script");
00324 }
00325 
00326 void compile_and_run(HSQUIRRELVM vm, std::istream& in,
00327                      const std::string& sourcename)
00328 {
00329   compile_script(vm, in, sourcename);
00330 
00331   SQInteger oldtop = sq_gettop(vm);
00332 
00333   try {
00334     sq_pushroottable(vm);
00335     if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
00336       throw SquirrelError(vm, "Couldn't start script");
00337   } catch(...) {
00338     sq_settop(vm, oldtop);
00339     throw;
00340   }
00341 
00342   // we can remove the closure in case the script was not suspended
00343   if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
00344     sq_settop(vm, oldtop-1);
00345   }
00346 }
00347 
00348 HSQOBJECT create_thread(HSQUIRRELVM vm)
00349 {
00350   HSQUIRRELVM new_vm = sq_newthread(vm, 64);
00351   if(new_vm == NULL)
00352     throw SquirrelError(vm, "Couldn't create new VM");
00353 
00354   HSQOBJECT vm_object;
00355   sq_resetobject(&vm_object);
00356   if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
00357     throw SquirrelError(vm, "Couldn't get squirrel thread from stack");
00358   sq_addref(vm, &vm_object);
00359 
00360   sq_pop(vm, 1);
00361 
00362   return vm_object;
00363 }
00364 
00365 HSQOBJECT vm_to_object(HSQUIRRELVM vm)
00366 {
00367   HSQOBJECT object;
00368   sq_resetobject(&object);
00369   object._unVal.pThread = vm;
00370   object._type = OT_THREAD;
00371 
00372   return object;
00373 }
00374 
00375 HSQUIRRELVM object_to_vm(HSQOBJECT object)
00376 {
00377   if(object._type != OT_THREAD)
00378     return NULL;
00379 
00380   return object._unVal.pThread;
00381 }
00382 
00383 // begin: serialization functions
00384 
00385 void store_float(HSQUIRRELVM vm, const char* name, float val)
00386 {
00387   sq_pushstring(vm, name, -1);
00388   sq_pushfloat(vm, val);
00389   if(SQ_FAILED(sq_createslot(vm, -3)))
00390     throw scripting::SquirrelError(vm, "Couldn't add float value to table");
00391 }
00392 
00393 void store_int(HSQUIRRELVM vm, const char* name, int val)
00394 {
00395   sq_pushstring(vm, name, -1);
00396   sq_pushinteger(vm, val);
00397   if(SQ_FAILED(sq_createslot(vm, -3)))
00398     throw scripting::SquirrelError(vm, "Couldn't add int value to table");
00399 }
00400 
00401 void store_string(HSQUIRRELVM vm, const char* name, const std::string& val)
00402 {
00403   sq_pushstring(vm, name, -1);
00404   sq_pushstring(vm, val.c_str(), val.length());
00405   if(SQ_FAILED(sq_createslot(vm, -3)))
00406     throw scripting::SquirrelError(vm, "Couldn't add float value to table");
00407 }
00408 
00409 void store_bool(HSQUIRRELVM vm, const char* name, bool val)
00410 {
00411   sq_pushstring(vm, name, -1);
00412   sq_pushbool(vm, val ? SQTrue : SQFalse);
00413   if(SQ_FAILED(sq_createslot(vm, -3)))
00414     throw scripting::SquirrelError(vm, "Couldn't add float value to table");
00415 }
00416 
00417 bool has_float(HSQUIRRELVM vm, const char* name)
00418 {
00419   sq_pushstring(vm, name, -1);
00420   if (SQ_FAILED(sq_get(vm, -2))) return false;
00421   sq_pop(vm, 1);
00422   return true;
00423 }
00424 
00425 bool has_int(HSQUIRRELVM vm, const char* name)
00426 {
00427   return has_float(vm, name);
00428 }
00429 
00430 bool has_string(HSQUIRRELVM vm, const char* name)
00431 {
00432   return has_float(vm, name);
00433 }
00434 
00435 bool has_bool(HSQUIRRELVM vm, const char* name)
00436 {
00437   return has_float(vm, name);
00438 }
00439 
00440 float read_float(HSQUIRRELVM vm, const char* name)
00441 {
00442   sq_pushstring(vm, name, -1);
00443   if(SQ_FAILED(sq_get(vm, -2))) {
00444     std::ostringstream msg;
00445     msg << "Couldn't get float value for '" << name << "' from table";
00446     throw scripting::SquirrelError(vm, msg.str());
00447   }
00448 
00449   float result;
00450   if(SQ_FAILED(sq_getfloat(vm, -1, &result))) {
00451     std::ostringstream msg;
00452     msg << "Couldn't get float value for '" << name << "' from table";
00453     throw scripting::SquirrelError(vm, msg.str());
00454   }
00455   sq_pop(vm, 1);
00456 
00457   return result;
00458 }
00459 
00460 int read_int(HSQUIRRELVM vm, const char* name)
00461 {
00462   sq_pushstring(vm, name, -1);
00463   if(SQ_FAILED(sq_get(vm, -2))) {
00464     std::ostringstream msg;
00465     msg << "Couldn't get int value for '" << name << "' from table";
00466     throw scripting::SquirrelError(vm, msg.str());
00467   }
00468 
00469   SQInteger result;
00470   if(SQ_FAILED(sq_getinteger(vm, -1, &result))) {
00471     std::ostringstream msg;
00472     msg << "Couldn't get int value for '" << name << "' from table";
00473     throw scripting::SquirrelError(vm, msg.str());
00474   }
00475   sq_pop(vm, 1);
00476 
00477   return result;
00478 }
00479 
00480 std::string read_string(HSQUIRRELVM vm, const char* name)
00481 {
00482   sq_pushstring(vm, name, -1);
00483   if(SQ_FAILED(sq_get(vm, -2))) {
00484     std::ostringstream msg;
00485     msg << "Couldn't get string value for '" << name << "' from table";
00486     throw scripting::SquirrelError(vm, msg.str());
00487   }
00488 
00489   const char* result;
00490   if(SQ_FAILED(sq_getstring(vm, -1, &result))) {
00491     std::ostringstream msg;
00492     msg << "Couldn't get string value for '" << name << "' from table";
00493     throw scripting::SquirrelError(vm, msg.str());
00494   }
00495   sq_pop(vm, 1);
00496 
00497   return std::string(result);
00498 }
00499 
00500 bool read_bool(HSQUIRRELVM vm, const char* name)
00501 {
00502   sq_pushstring(vm, name, -1);
00503   if(SQ_FAILED(sq_get(vm, -2))) {
00504     std::ostringstream msg;
00505     msg << "Couldn't get bool value for '" << name << "' from table";
00506     throw scripting::SquirrelError(vm, msg.str());
00507   }
00508 
00509   SQBool result;
00510   if(SQ_FAILED(sq_getbool(vm, -1, &result))) {
00511     std::ostringstream msg;
00512     msg << "Couldn't get bool value for '" << name << "' from table";
00513     throw scripting::SquirrelError(vm, msg.str());
00514   }
00515   sq_pop(vm, 1);
00516 
00517   return result == SQTrue;
00518 }
00519 
00520 bool get_float(HSQUIRRELVM vm, const char* name, float& val) {
00521   if (!has_float(vm, name)) return false;
00522   val = read_float(vm, name);
00523   return true;
00524 }
00525 
00526 bool get_int(HSQUIRRELVM vm, const char* name, int& val) {
00527   if (!has_int(vm, name)) return false;
00528   val = read_int(vm, name);
00529   return true;
00530 }
00531 
00532 bool get_string(HSQUIRRELVM vm, const char* name, std::string& val) {
00533   if (!has_string(vm, name)) return false;
00534   val = read_string(vm, name);
00535   return true;
00536 }
00537 
00538 bool get_bool(HSQUIRRELVM vm, const char* name, bool& val) {
00539   if (!has_bool(vm, name)) return false;
00540   val = read_bool(vm, name);
00541   return true;
00542 }
00543 
00544 // end: serialization functions
00545 
00546 }
00547 
00548 /* EOF */

Generated on Mon Jun 9 03:38:21 2014 for SuperTux by  doxygen 1.5.1