00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
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
00087 register_supertux_wrapper(global_vm);
00088
00089 sq_pop(global_vm, 1);
00090
00091
00092 sq_setprintfunc(global_vm, printfunc);
00093
00094 sqstd_seterrorhandlers(global_vm);
00095
00096
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);
00168 while(SQ_SUCCEEDED(sq_next(v,i-1)))
00169 {
00170 if (!first) {
00171 os << ", ";
00172 }
00173 first = false;
00174
00175
00176 os << squirrel2string(v, -2) << " => "
00177 << squirrel2string(v, -1);
00178
00179 sq_pop(v,2);
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);
00189 while(SQ_SUCCEEDED(sq_next(v,i-1)))
00190 {
00191 if (!first) {
00192 os << ", ";
00193 }
00194 first = false;
00195
00196
00197
00198 os << squirrel2string(v, -1);
00199
00200 sq_pop(v,2);
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
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
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
00545
00546 }
00547
00548