1 // 2 // SuperTuxKart - a fun racing game with go-kart 3 // Copyright (C) 2014-2015 SuperTuxKart Team 4 // 5 // This program is free software; you can redistribute it and/or 6 // modify it under the terms of the GNU General Public License 7 // as published by the Free Software Foundation; either version 3 8 // of the License, or (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with this program; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 extern "C" 20 { 21 #include <mcpp_lib.h> 22 } 23 #include <assert.h> 24 #include <angelscript.h> 25 #include "io/file_manager.hpp" 26 #include "karts/kart.hpp" 27 #include "modes/world.hpp" 28 #include "scriptengine/aswrappedcall.hpp" 29 #include "scriptengine/script_audio.hpp" 30 #include "scriptengine/script_challenges.hpp" 31 #include "scriptengine/script_kart.hpp" 32 #include "scriptengine/script_engine.hpp" 33 #include "scriptengine/script_gui.hpp" 34 #include "scriptengine/script_physics.hpp" 35 #include "scriptengine/script_track.hpp" 36 #include "scriptengine/script_utils.hpp" 37 #include "scriptengine/scriptstdstring.hpp" 38 #include "scriptengine/scriptvec3.hpp" 39 #include "scriptengine/scriptarray.hpp" 40 #include <string.h> 41 #include "states_screens/dialogs/tutorial_message_dialog.hpp" 42 #include "tracks/track_object_manager.hpp" 43 #include "tracks/track.hpp" 44 #include "utils/file_utils.hpp" 45 #include "utils/string_utils.hpp" 46 #include "utils/profiler.hpp" 47 48 49 using namespace Scripting; 50 51 namespace Scripting 52 { 53 const char* MODULE_ID_MAIN_SCRIPT_FILE = "main"; 54 AngelScript_ErrorCallback(const asSMessageInfo * msg,void * param)55 void AngelScript_ErrorCallback (const asSMessageInfo *msg, void *param) 56 { 57 const char *type = "ERR "; 58 if (msg->type == asMSGTYPE_WARNING) 59 type = "WARN"; 60 else if (msg->type == asMSGTYPE_INFORMATION) 61 type = "INFO"; 62 63 Log::warn("Scripting", "%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message); 64 } 65 66 67 //Constructor, creates a new Scripting Engine using AngelScript ScriptEngine()68 ScriptEngine::ScriptEngine() 69 { 70 // Create the script engine 71 m_engine = asCreateScriptEngine(ANGELSCRIPT_VERSION); 72 if (m_engine == NULL) 73 { 74 Log::fatal("Scripting", "Failed to create script engine."); 75 } 76 77 // The script compiler will write any compiler messages to the callback. 78 m_engine->SetMessageCallback(asFUNCTION(AngelScript_ErrorCallback), 0, asCALL_CDECL); 79 80 // Configure the script engine with all the functions, 81 // and variables that the script should be able to use. 82 configureEngine(m_engine); 83 } 84 ~ScriptEngine()85 ScriptEngine::~ScriptEngine() 86 { 87 // Release the engine 88 m_pending_timeouts.clearAndDeleteAll(); 89 m_engine->DiscardModule(MODULE_ID_MAIN_SCRIPT_FILE); 90 m_engine->Release(); 91 } 92 93 94 95 /** Get Script By it's file name 96 * \param string scriptname = name of script to get 97 * \return The corresponding script 98 */ getScript(std::string script_path)99 std::string getScript(std::string script_path) 100 { 101 std::string script_file = FileUtils::getPortableReadingPath(script_path); 102 if (!file_manager->fileExists(script_file)) 103 { 104 #ifndef SERVER_ONLY 105 Log::debug("Scripting", "File does not exist : %s", script_path.c_str()); 106 #endif 107 return ""; 108 } 109 110 // libmcpp ignores the first argument (like real main which is the exe) 111 std::string cmd1 = "mcpp"; 112 std::string int_version = 113 StringUtils::toString(StringUtils::versionToInt(STK_VERSION)); 114 // Preprocessing (atm add stk version) 115 std::string cmd2 = "-DSTK_VERSION="; 116 cmd2 += int_version; 117 // -P Don't output #line lines. 118 std::string cmd3 = "-P"; 119 // -j Don't output the source line in diagnostics. 120 std::string cmd4 = "-j"; 121 // -e <encoding> Change the default multi-byte character encoding to one of: 122 // euc_jp, gb2312, ksc5601, big5, sjis, iso2022_jp, utf8. 123 std::string cmd5 = "-e"; 124 std::string cmd6 = "utf8"; 125 std::string cmd7 = script_file; 126 std::vector<char*> all_cmds; 127 all_cmds.push_back(&cmd1[0]); 128 all_cmds.push_back(&cmd2[0]); 129 all_cmds.push_back(&cmd3[0]); 130 all_cmds.push_back(&cmd4[0]); 131 all_cmds.push_back(&cmd5[0]); 132 all_cmds.push_back(&cmd6[0]); 133 all_cmds.push_back(&cmd7[0]); 134 mcpp_use_mem_buffers(1); 135 mcpp_lib_main(all_cmds.size(), all_cmds.data()); 136 137 char* err = mcpp_get_mem_buffer((OUTDEST)1/*error buffer*/); 138 bool has_error = false; 139 if (err) 140 { 141 std::string total = err; 142 auto errs = StringUtils::split(total, '\n'); 143 for (auto& e : errs) 144 { 145 if (e.find("warning: Converted [CR+LF] to [LF]") != 146 std::string::npos) 147 continue; 148 149 if (e.find("fatal:") != std::string::npos || 150 e.find("error:") != std::string::npos) 151 { 152 has_error = true; 153 Log::error("Scripting preprocessing", "%s", e.c_str()); 154 } 155 else 156 { 157 Log::warn("Scripting preprocessing", "%s", e.c_str()); 158 } 159 } 160 } 161 162 std::string result; 163 if (!has_error) 164 { 165 char* buf = mcpp_get_mem_buffer((OUTDEST)0/*output buffer*/); 166 if (buf) 167 result = buf; 168 } 169 170 // Calling this again causes the memory buffers to be freed. 171 mcpp_use_mem_buffers(1); 172 return result; 173 } 174 175 //----------------------------------------------------------------------------- 176 evalScript(std::string script_fragment)177 void ScriptEngine::evalScript(std::string script_fragment) 178 { 179 script_fragment = "void evalScript_main() { \n" + script_fragment + "\n}"; 180 181 asIScriptModule* mod = m_engine->GetModule(MODULE_ID_MAIN_SCRIPT_FILE, asGM_ONLY_IF_EXISTS); 182 183 asIScriptFunction* func; 184 int r = mod->CompileFunction("eval", script_fragment.c_str(), 0, 0, &func); 185 if (r < 0) 186 { 187 Log::error("Scripting", "evalScript: CompileFunction() failed"); 188 return; 189 } 190 191 asIScriptContext *ctx = m_engine->CreateContext(); 192 if (ctx == NULL) 193 { 194 Log::error("Scripting", "evalScript: Failed to create the context."); 195 //m_engine->Release(); 196 return; 197 } 198 199 r = ctx->Prepare(func); 200 if (r < 0) 201 { 202 Log::error("Scripting", "evalScript: Failed to prepare the context."); 203 ctx->Release(); 204 return; 205 } 206 207 // Execute the function 208 r = ctx->Execute(); 209 if (r != asEXECUTION_FINISHED) 210 { 211 // The execution didn't finish as we had planned. Determine why. 212 if (r == asEXECUTION_ABORTED) 213 { 214 Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out."); 215 } 216 else if (r == asEXECUTION_EXCEPTION) 217 { 218 Log::error("Scripting", "The script ended with an exception."); 219 } 220 else 221 { 222 Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r); 223 } 224 } 225 226 ctx->Release(); 227 func->Release(); 228 } 229 230 //----------------------------------------------------------------------------- 231 runDelegate(asIScriptFunction * delegate)232 void ScriptEngine::runDelegate(asIScriptFunction* delegate) 233 { 234 asIScriptContext *ctx = m_engine->CreateContext(); 235 if (ctx == NULL) 236 { 237 Log::error("Scripting", "runMethod: Failed to create the context."); 238 //m_engine->Release(); 239 return; 240 } 241 242 int r = ctx->Prepare(delegate); 243 if (r < 0) 244 { 245 Log::error("Scripting", "runMethod: Failed to prepare the context."); 246 ctx->Release(); 247 return; 248 } 249 250 // Execute the function 251 r = ctx->Execute(); 252 if (r != asEXECUTION_FINISHED) 253 { 254 // The execution didn't finish as we had planned. Determine why. 255 if (r == asEXECUTION_ABORTED) 256 { 257 Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out."); 258 } 259 else if (r == asEXECUTION_EXCEPTION) 260 { 261 Log::error("Scripting", "The script ended with an exception : (line %i) %s", 262 ctx->GetExceptionLineNumber(), 263 ctx->GetExceptionString()); 264 } 265 else 266 { 267 Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r); 268 } 269 } 270 271 ctx->Release(); 272 } 273 274 //----------------------------------------------------------------------------- 275 276 /* 277 void ScriptEngine::runMethod(asIScriptObject* obj, std::string methodName) 278 { 279 asITypeInfo* type = obj->GetObjectType(); 280 asIScriptFunction* method = type->GetMethodByName(methodName.c_str()); 281 if (method == NULL) 282 Log::error("Scripting", ("runMethod: object does not implement method " + methodName).c_str()); 283 284 285 asIScriptContext *ctx = m_engine->CreateContext(); 286 if (ctx == NULL) 287 { 288 Log::error("Scripting", "runMethod: Failed to create the context."); 289 //m_engine->Release(); 290 return; 291 } 292 293 int r = ctx->Prepare(method); 294 if (r < 0) 295 { 296 Log::error("Scripting", "runMethod: Failed to prepare the context."); 297 ctx->Release(); 298 return; 299 } 300 301 // Execute the function 302 r = ctx->Execute(); 303 if (r != asEXECUTION_FINISHED) 304 { 305 // The execution didn't finish as we had planned. Determine why. 306 if (r == asEXECUTION_ABORTED) 307 { 308 Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out."); 309 } 310 else if (r == asEXECUTION_EXCEPTION) 311 { 312 Log::error("Scripting", "The script ended with an exception."); 313 } 314 else 315 { 316 Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r); 317 } 318 } 319 320 ctx->Release(); 321 } 322 */ 323 //----------------------------------------------------------------------------- 324 325 /** runs the specified script 326 * \param string scriptName = name of script to run 327 */ runFunction(bool warn_if_not_found,std::string function_name)328 void ScriptEngine::runFunction(bool warn_if_not_found, std::string function_name) 329 { 330 std::function<void(asIScriptContext*)> callback; 331 std::function<void(asIScriptContext*)> get_return_value; 332 runFunction(warn_if_not_found, function_name, callback, get_return_value); 333 } 334 335 //----------------------------------------------------------------------------- 336 runFunction(bool warn_if_not_found,std::string function_name,std::function<void (asIScriptContext *)> callback)337 void ScriptEngine::runFunction(bool warn_if_not_found, std::string function_name, 338 std::function<void(asIScriptContext*)> callback) 339 { 340 std::function<void(asIScriptContext*)> get_return_value; 341 runFunction(warn_if_not_found, function_name, callback, get_return_value); 342 } 343 344 //----------------------------------------------------------------------------- 345 346 /** runs the specified script 347 * \param string scriptName = name of script to run 348 */ runFunction(bool warn_if_not_found,std::string function_name,std::function<void (asIScriptContext *)> callback,std::function<void (asIScriptContext *)> get_return_value)349 void ScriptEngine::runFunction(bool warn_if_not_found, std::string function_name, 350 std::function<void(asIScriptContext*)> callback, 351 std::function<void(asIScriptContext*)> get_return_value) 352 { 353 int r; //int for error checking 354 355 asIScriptFunction *func; 356 357 // TODO: allow splitting in multiple files 358 std::string script_filename = "scripting.as"; 359 auto cached_function = m_functions_cache.find(function_name); 360 if (cached_function == m_functions_cache.end()) 361 { 362 // Find the function for the function we want to execute. 363 // This is how you call a normal function with arguments 364 // asIScriptFunction *func = engine->GetModule(0)->GetFunctionByDecl("void func(arg1Type, arg2Type)"); 365 asIScriptModule* module = m_engine->GetModule(MODULE_ID_MAIN_SCRIPT_FILE); 366 367 if (module == NULL) 368 { 369 #ifndef SERVER_ONLY 370 if (warn_if_not_found) 371 Log::warn("Scripting", "Scripting function was not found : %s (module not found)", function_name.c_str()); 372 else 373 Log::debug("Scripting", "Scripting function was not found : %s (module not found)", function_name.c_str()); 374 #endif 375 m_functions_cache[function_name] = NULL; // remember that this function is unavailable 376 return; 377 } 378 379 func = module->GetFunctionByDecl(function_name.c_str()); 380 381 if (func == NULL) 382 { 383 #ifndef SERVER_ONLY 384 if (warn_if_not_found) 385 Log::warn("Scripting", "Scripting function was not found : %s", function_name.c_str()); 386 else 387 Log::debug("Scripting", "Scripting function was not found : %s", function_name.c_str()); 388 #endif 389 m_functions_cache[function_name] = NULL; // remember that this function is unavailable 390 return; 391 } 392 393 m_functions_cache[function_name] = func; 394 func->AddRef(); 395 } 396 else 397 { 398 // Script present in cache 399 func = cached_function->second; 400 } 401 402 if (func == NULL) 403 { 404 if (warn_if_not_found) 405 Log::warn("Scripting", "Scripting function was not found : %s", function_name.c_str()); 406 return; // function unavailable 407 } 408 409 // Create a context that will execute the script. 410 asIScriptContext *ctx = m_engine->CreateContext(); 411 if (ctx == NULL) 412 { 413 Log::error("Scripting", "Failed to create the context."); 414 //m_engine->Release(); 415 return; 416 } 417 418 // Prepare the script context with the function we wish to execute. Prepare() 419 // must be called on the context before each new script function that will be 420 // executed. Note, that if because we intend to execute the same function 421 // several times, we will store the function returned by 422 // GetFunctionByDecl(), so that this relatively slow call can be skipped. 423 r = ctx->Prepare(func); 424 if (r < 0) 425 { 426 Log::error("Scripting", "Failed to prepare the context."); 427 ctx->Release(); 428 //m_engine->Release(); 429 return; 430 } 431 432 // Here, we can pass parameters to the script functions. 433 //ctx->setArgType(index, value); 434 //for example : ctx->SetArgFloat(0, 3.14159265359f); 435 436 if (callback) 437 callback(ctx); 438 439 // Execute the function 440 r = ctx->Execute(); 441 if (r != asEXECUTION_FINISHED) 442 { 443 // The execution didn't finish as we had planned. Determine why. 444 if (r == asEXECUTION_ABORTED) 445 { 446 Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out."); 447 } 448 else if (r == asEXECUTION_EXCEPTION) 449 { 450 Log::error("Scripting", "The script ended with an exception."); 451 452 // Write some information about the script exception 453 //asIScriptFunction *func = ctx->GetExceptionFunction(); 454 //std::cout << "func: " << func->GetDeclaration() << std::endl; 455 //std::cout << "modl: " << func->GetModuleName() << std::endl; 456 //std::cout << "sect: " << func->GetScriptSectionName() << std::endl; 457 //std::cout << "line: " << ctx->GetExceptionLineNumber() << std::endl; 458 //std::cout << "desc: " << ctx->GetExceptionString() << std::endl; 459 } 460 else 461 { 462 Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r); 463 } 464 } 465 else 466 { 467 // Retrieve the return value from the context here (for scripts that return values) 468 // <type> returnValue = ctx->getReturnType(); for example 469 //float returnValue = ctx->GetReturnFloat(); 470 471 if (get_return_value) 472 get_return_value(ctx); 473 } 474 475 // We must release the contexts when no longer using them 476 ctx->Release(); 477 } 478 479 //----------------------------------------------------------------------------- 480 cleanupCache()481 void ScriptEngine::cleanupCache() 482 { 483 for (auto curr : m_functions_cache) 484 { 485 if (curr.second != NULL) 486 curr.second->Release(); 487 } 488 m_functions_cache.clear(); 489 m_engine->DiscardModule(MODULE_ID_MAIN_SCRIPT_FILE); 490 } 491 492 //----------------------------------------------------------------------------- 493 /** Configures the script engine by binding functions, enums 494 * \param asIScriptEngine engine = engine to configure 495 */ configureEngine(asIScriptEngine * engine)496 void ScriptEngine::configureEngine(asIScriptEngine *engine) 497 { 498 // Register the script string type 499 RegisterStdString(engine); //register std::string 500 RegisterVec3(engine); //register Vec3 501 RegisterScriptArray(engine, true); 502 503 Scripting::Track::registerScriptFunctions(m_engine); 504 Scripting::Challenges::registerScriptFunctions(m_engine); 505 Scripting::Kart::registerScriptFunctions(m_engine); 506 Scripting::Kart::registerScriptEnums(m_engine); 507 Scripting::Physics::registerScriptFunctions(m_engine); 508 Scripting::Utils::registerScriptFunctions(m_engine); 509 Scripting::GUI::registerScriptEnums(m_engine); 510 Scripting::GUI::registerScriptFunctions(m_engine); 511 Scripting::Audio::registerScriptFunctions(m_engine); 512 513 // It is possible to register the functions, properties, and types in 514 // configuration groups as well. When compiling the scripts it can then 515 // be defined which configuration groups should be available for that 516 // script. If necessary a configuration group can also be removed from 517 // the engine, so that the engine configuration could be changed 518 // without having to recompile all the scripts. 519 } 520 521 //----------------------------------------------------------------------------- 522 loadScript(std::string script_path,bool clear_previous)523 bool ScriptEngine::loadScript(std::string script_path, bool clear_previous) 524 { 525 int r; 526 527 std::string script = getScript(script_path); 528 if (script.size() == 0) 529 { 530 // No such file 531 return false; 532 } 533 534 // Add the script sections that will be compiled into executable code. 535 // If we want to combine more than one file into the same script, then 536 // we can call AddScriptSection() several times for the same module and 537 // the script engine will treat them all as if they were one. The script 538 // section name, will allow us to localize any errors in the script code. 539 asIScriptModule *mod = m_engine->GetModule(MODULE_ID_MAIN_SCRIPT_FILE, 540 clear_previous ? asGM_ALWAYS_CREATE : asGM_CREATE_IF_NOT_EXISTS); 541 r = mod->AddScriptSection("script", &script[0], script.size()); 542 if (r < 0) 543 { 544 Log::error("Scripting", "AddScriptSection() failed"); 545 return false; 546 } 547 548 return true; 549 } 550 551 //----------------------------------------------------------------------------- 552 compileLoadedScripts()553 bool ScriptEngine::compileLoadedScripts() 554 { 555 int r; 556 asIScriptModule *mod = m_engine->GetModule(MODULE_ID_MAIN_SCRIPT_FILE, asGM_CREATE_IF_NOT_EXISTS); 557 558 // Compile the script. If there are any compiler messages they will 559 // be written to the message stream that we set right after creating the 560 // script engine. If there are no errors, and no warnings, nothing will 561 // be written to the stream. 562 r = mod->Build(); 563 if (r < 0) 564 { 565 Log::error("Scripting", "Build() failed"); 566 return false; 567 } 568 569 // The engine doesn't keep a copy of the script sections after Build() has 570 // returned. So if the script needs to be recompiled, then all the script 571 // sections must be added again. 572 573 // If we want to have several scripts executing at different times but 574 // that have no direct relation with each other, then we can compile them 575 // into separate script modules. Each module uses their own namespace and 576 // scope, so function names, and global variables will not conflict with 577 // each other. 578 579 return true; 580 } 581 582 //----------------------------------------------------------------------------- 583 PendingTimeout(double time,asIScriptFunction * callback_delegate)584 PendingTimeout::PendingTimeout(double time, asIScriptFunction* callback_delegate) 585 { 586 m_time = time; 587 m_callback_delegate = callback_delegate; 588 589 #if ANGELSCRIPT_VERSION < 23300 590 if (strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")) 591 { 592 callback_delegate->AddRef(); 593 } 594 #endif 595 } 596 597 //----------------------------------------------------------------------------- 598 ~PendingTimeout()599 PendingTimeout::~PendingTimeout() 600 { 601 if (m_callback_delegate != NULL) 602 { 603 m_callback_delegate->Release(); 604 } 605 } 606 607 //----------------------------------------------------------------------------- 608 addPendingTimeout(double time,const std::string & callback_name)609 void ScriptEngine::addPendingTimeout(double time, const std::string& callback_name) 610 { 611 m_pending_timeouts.push_back(new PendingTimeout(time, callback_name)); 612 } 613 614 //----------------------------------------------------------------------------- 615 addPendingTimeout(double time,asIScriptFunction * delegate)616 void ScriptEngine::addPendingTimeout(double time, asIScriptFunction* delegate) 617 { 618 m_pending_timeouts.push_back(new PendingTimeout(time, delegate)); 619 } 620 621 //----------------------------------------------------------------------------- 622 update(float dt)623 void ScriptEngine::update(float dt) 624 { 625 for (int i = m_pending_timeouts.size() - 1; i >= 0; i--) 626 { 627 PendingTimeout& curr = m_pending_timeouts[i]; 628 curr.m_time -= dt; 629 if (curr.m_time <= 0.0) 630 { 631 if (curr.m_callback_delegate != NULL) 632 { 633 runDelegate(curr.m_callback_delegate); 634 } 635 else 636 { 637 runFunction(true, "void " + curr.m_callback_name + "()"); 638 } 639 640 m_pending_timeouts.erase(i); 641 } 642 } 643 } 644 } 645