Work on scripting
This commit is contained in:
parent
e23f854845
commit
080936f144
17
data/gui/scripting_console.stkgui
Normal file
17
data/gui/scripting_console.stkgui
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<stkgui>
|
||||
<div x="2%" y="10%" width="96%" height="80%" layout="vertical-row" >
|
||||
|
||||
<label raw_text="Run script"/>
|
||||
|
||||
<textbox id="textfield" width="75%" I18N="In the 'add new grand prix' dialog" align="center"/>
|
||||
|
||||
<spacer height="20" width="20" />
|
||||
|
||||
<div align="center" height="fit" width="100%" layout="horizontal-row">
|
||||
<button id="run" raw_text="Run" align="center" proportion="1"/>
|
||||
<spacer height="20" width="20" />
|
||||
<button id="close" raw_text="Close" align="center" proportion="1"/>
|
||||
</div>
|
||||
</div>
|
||||
</stkgui>
|
@ -171,7 +171,7 @@ void Physics::update(float dt)
|
||||
Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine();
|
||||
int kartid1 = p->getUserPointer(0)->getPointerKart()->getWorldKartId();
|
||||
int kartid2 = p->getUserPointer(1)->getPointerKart()->getWorldKartId();
|
||||
script_engine->runScript("void onKartKartCollision(int, int)",
|
||||
script_engine->runFunction("void onKartKartCollision(int, int)",
|
||||
[=](asIScriptContext* ctx) {
|
||||
ctx->SetArgDWord(0, kartid1);
|
||||
ctx->SetArgDWord(1, kartid2);
|
||||
@ -189,7 +189,7 @@ void Physics::update(float dt)
|
||||
std::string obj_id = p->getUserPointer(0)->getPointerPhysicalObject()->getID();
|
||||
|
||||
// TODO: pass obj_id as arguent
|
||||
script_engine->runScript("void onKartObjectCollision(int)",
|
||||
script_engine->runFunction("void onKartObjectCollision(int)",
|
||||
[=](asIScriptContext* ctx) {
|
||||
ctx->SetArgDWord(0, kartId);
|
||||
});
|
||||
@ -260,7 +260,7 @@ void Physics::update(float dt)
|
||||
// p->getUserPointer(1)->getPointerPhysicalObject()->getID(),
|
||||
// "item"
|
||||
//);
|
||||
script_engine->runScript("void onItemObjectCollision()");
|
||||
script_engine->runFunction("void onItemObjectCollision()");
|
||||
p->getUserPointer(0)->getPointerFlyable()
|
||||
->hit(NULL, p->getUserPointer(1)->getPointerPhysicalObject());
|
||||
PhysicalObject* obj = p->getUserPointer(1)->getPointerPhysicalObject();
|
||||
|
@ -38,308 +38,372 @@ using namespace Scripting;
|
||||
|
||||
namespace Scripting
|
||||
{
|
||||
const char* MODULE_ID_MAIN_SCRIPT_FILE = "main";
|
||||
const char* MODULE_ID_EVAL = "eval";
|
||||
|
||||
|
||||
void AngelScript_ErrorCallback (const asSMessageInfo *msg, void *param)
|
||||
{
|
||||
const char *type = "ERR ";
|
||||
if (msg->type == asMSGTYPE_WARNING)
|
||||
type = "WARN";
|
||||
else if (msg->type == asMSGTYPE_INFORMATION)
|
||||
type = "INFO";
|
||||
|
||||
Log::warn("Scripting", "%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
|
||||
}
|
||||
|
||||
|
||||
//Constructor, creates a new Scripting Engine using AngelScript
|
||||
ScriptEngine::ScriptEngine()
|
||||
{
|
||||
// Create the script engine
|
||||
m_engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
|
||||
if (m_engine == NULL)
|
||||
void AngelScript_ErrorCallback (const asSMessageInfo *msg, void *param)
|
||||
{
|
||||
Log::fatal("Scripting", "Failed to create script engine.");
|
||||
const char *type = "ERR ";
|
||||
if (msg->type == asMSGTYPE_WARNING)
|
||||
type = "WARN";
|
||||
else if (msg->type == asMSGTYPE_INFORMATION)
|
||||
type = "INFO";
|
||||
|
||||
Log::warn("Scripting", "%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
|
||||
}
|
||||
|
||||
// The script compiler will write any compiler messages to the callback.
|
||||
m_engine->SetMessageCallback(asFUNCTION(AngelScript_ErrorCallback), 0, asCALL_CDECL);
|
||||
|
||||
// Configure the script engine with all the functions,
|
||||
// and variables that the script should be able to use.
|
||||
configureEngine(m_engine);
|
||||
}
|
||||
|
||||
ScriptEngine::~ScriptEngine()
|
||||
{
|
||||
// Release the engine
|
||||
m_engine->Release();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Get Script By it's file name
|
||||
* \param string scriptname = name of script to get
|
||||
* \return The corresponding script
|
||||
*/
|
||||
std::string getScript(std::string scriptName)
|
||||
{
|
||||
std::string script_dir = file_manager->getAsset(FileManager::SCRIPT, "");
|
||||
script_dir += World::getWorld()->getTrack()->getIdent() + "/";
|
||||
|
||||
// TODO: allow splitting in multiple files
|
||||
script_dir += "scripting.as";
|
||||
FILE *f = fopen(script_dir.c_str(), "rb");
|
||||
if (f == NULL)
|
||||
//Constructor, creates a new Scripting Engine using AngelScript
|
||||
ScriptEngine::ScriptEngine()
|
||||
{
|
||||
Log::debug("Scripting", "File does not exist : {0}.as", scriptName.c_str());
|
||||
return "";
|
||||
// Create the script engine
|
||||
m_engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
|
||||
if (m_engine == NULL)
|
||||
{
|
||||
Log::fatal("Scripting", "Failed to create script engine.");
|
||||
}
|
||||
|
||||
// The script compiler will write any compiler messages to the callback.
|
||||
m_engine->SetMessageCallback(asFUNCTION(AngelScript_ErrorCallback), 0, asCALL_CDECL);
|
||||
|
||||
// Configure the script engine with all the functions,
|
||||
// and variables that the script should be able to use.
|
||||
configureEngine(m_engine);
|
||||
}
|
||||
|
||||
// Determine the size of the file
|
||||
fseek(f, 0, SEEK_END);
|
||||
int len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
// Read the entire file
|
||||
std::string script;
|
||||
script.resize(len);
|
||||
int c = fread(&script[0], len, 1, f);
|
||||
fclose(f);
|
||||
if (c == NULL)
|
||||
ScriptEngine::~ScriptEngine()
|
||||
{
|
||||
Log::error("Scripting", "Failed to load script file.");
|
||||
return "";
|
||||
// Release the engine
|
||||
m_engine->Release();
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** runs the specified script
|
||||
* \param string scriptName = name of script to run
|
||||
*/
|
||||
void ScriptEngine::runScript(std::string scriptName)
|
||||
{
|
||||
std::function<void(asIScriptContext*)> callback;
|
||||
runScript(scriptName, callback);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** runs the specified script
|
||||
* \param string scriptName = name of script to run
|
||||
*/
|
||||
void ScriptEngine::runScript(std::string scriptName, std::function<void(asIScriptContext*)> callback)
|
||||
{
|
||||
int r; //int for error checking
|
||||
|
||||
asIScriptFunction *func;
|
||||
auto cached_script = m_script_cache.find(scriptName);
|
||||
if (cached_script == m_script_cache.end())
|
||||
/** Get Script By it's file name
|
||||
* \param string scriptname = name of script to get
|
||||
* \return The corresponding script
|
||||
*/
|
||||
std::string getScript(std::string fileName)
|
||||
{
|
||||
// Compile the script code
|
||||
Log::debug("Scripting", "Compiling script '%s' (was not in cache)", scriptName.c_str());
|
||||
r = compileScript(m_engine, scriptName);
|
||||
std::string script_dir = file_manager->getAsset(FileManager::SCRIPT, "");
|
||||
script_dir += World::getWorld()->getTrack()->getIdent() + "/";
|
||||
|
||||
script_dir += fileName;
|
||||
FILE *f = fopen(script_dir.c_str(), "rb");
|
||||
if (f == NULL)
|
||||
{
|
||||
Log::debug("Scripting", "File does not exist : {0}.as", fileName.c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
// Determine the size of the file
|
||||
fseek(f, 0, SEEK_END);
|
||||
int len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
// Read the entire file
|
||||
std::string script;
|
||||
script.resize(len);
|
||||
int c = fread(&script[0], len, 1, f);
|
||||
fclose(f);
|
||||
if (c == NULL)
|
||||
{
|
||||
Log::error("Scripting", "Failed to load script file.");
|
||||
return "";
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ScriptEngine::evalScript(std::string script_fragment)
|
||||
{
|
||||
script_fragment = "void evalScript_main() { \n" + script_fragment + "\n}";
|
||||
|
||||
asIScriptModule* mod = m_engine->GetModule(MODULE_ID_EVAL, asGM_ALWAYS_CREATE);
|
||||
int r = mod->AddScriptSection("script", &script_fragment[0], script_fragment.size());
|
||||
if (r < 0)
|
||||
{
|
||||
Log::debug("Scripting", "Script '%s' is not available", scriptName.c_str());
|
||||
m_script_cache[scriptName] = NULL; // remember that this script is unavailable
|
||||
Log::error("Scripting", "evalScript: AddScriptSection() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the function for the function we want to execute.
|
||||
//This is how you call a normal function with arguments
|
||||
//asIScriptFunction *func = engine->GetModule(0)->GetFunctionByDecl("void func(arg1Type, arg2Type)");
|
||||
func = registerScriptCallbacks(m_engine, scriptName);
|
||||
|
||||
if (func == NULL)
|
||||
r = mod->Build();
|
||||
if (r < 0)
|
||||
{
|
||||
Log::debug("Scripting", "Scripting function was not found : %s", scriptName.c_str());
|
||||
m_script_cache[scriptName] = NULL; // remember that this script is unavailable
|
||||
Log::error("Scripting", "evalScript: Build() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
//CACHE UPDATE
|
||||
m_script_cache[scriptName] = func;
|
||||
func->AddRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Script present in cache
|
||||
func = cached_script->second;
|
||||
}
|
||||
asIScriptFunction* func = m_engine->GetModule(MODULE_ID_EVAL)
|
||||
->GetFunctionByDecl("void evalScript_main()");
|
||||
|
||||
if (func == NULL)
|
||||
{
|
||||
return; // script unavailable
|
||||
}
|
||||
asIScriptContext *ctx = m_engine->CreateContext();
|
||||
if (ctx == NULL)
|
||||
{
|
||||
Log::error("Scripting", "evalScript: Failed to create the context.");
|
||||
//m_engine->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
r = ctx->Prepare(func);
|
||||
if (r < 0)
|
||||
{
|
||||
Log::error("Scripting", "evalScript: Failed to prepare the context.");
|
||||
ctx->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a context that will execute the script.
|
||||
asIScriptContext *ctx = m_engine->CreateContext();
|
||||
if (ctx == NULL)
|
||||
{
|
||||
Log::error("Scripting", "Failed to create the context.");
|
||||
//m_engine->Release();
|
||||
return;
|
||||
}
|
||||
// Execute the function
|
||||
r = ctx->Execute();
|
||||
if (r != asEXECUTION_FINISHED)
|
||||
{
|
||||
// The execution didn't finish as we had planned. Determine why.
|
||||
if (r == asEXECUTION_ABORTED)
|
||||
{
|
||||
Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out.");
|
||||
}
|
||||
else if (r == asEXECUTION_EXCEPTION)
|
||||
{
|
||||
Log::error("Scripting", "The script ended with an exception.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the script context with the function we wish to execute. Prepare()
|
||||
// must be called on the context before each new script function that will be
|
||||
// executed. Note, that if because we intend to execute the same function
|
||||
// several times, we will store the function returned by
|
||||
// GetFunctionByDecl(), so that this relatively slow call can be skipped.
|
||||
r = ctx->Prepare(func);
|
||||
if (r < 0)
|
||||
{
|
||||
Log::error("Scripting", "Failed to prepare the context.");
|
||||
ctx->Release();
|
||||
//m_engine->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Here, we can pass parameters to the script functions.
|
||||
//ctx->setArgType(index, value);
|
||||
//for example : ctx->SetArgFloat(0, 3.14159265359f);
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
if (callback)
|
||||
callback(ctx);
|
||||
|
||||
// Execute the function
|
||||
r = ctx->Execute();
|
||||
if (r != asEXECUTION_FINISHED)
|
||||
/** runs the specified script
|
||||
* \param string scriptName = name of script to run
|
||||
*/
|
||||
void ScriptEngine::runFunction(std::string function_name)
|
||||
{
|
||||
// The execution didn't finish as we had planned. Determine why.
|
||||
if (r == asEXECUTION_ABORTED)
|
||||
{
|
||||
Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out.");
|
||||
}
|
||||
else if (r == asEXECUTION_EXCEPTION)
|
||||
{
|
||||
Log::error("Scripting", "The script ended with an exception.");
|
||||
std::function<void(asIScriptContext*)> callback;
|
||||
runFunction(function_name, callback);
|
||||
}
|
||||
|
||||
// Write some information about the script exception
|
||||
asIScriptFunction *func = ctx->GetExceptionFunction();
|
||||
//std::cout << "func: " << func->GetDeclaration() << std::endl;
|
||||
//std::cout << "modl: " << func->GetModuleName() << std::endl;
|
||||
//std::cout << "sect: " << func->GetScriptSectionName() << std::endl;
|
||||
//std::cout << "line: " << ctx->GetExceptionLineNumber() << std::endl;
|
||||
//std::cout << "desc: " << ctx->GetExceptionString() << std::endl;
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** runs the specified script
|
||||
* \param string scriptName = name of script to run
|
||||
*/
|
||||
void ScriptEngine::runFunction(std::string function_name, std::function<void(asIScriptContext*)> callback)
|
||||
{
|
||||
int r; //int for error checking
|
||||
|
||||
asIScriptFunction *func;
|
||||
|
||||
// TODO: allow splitting in multiple files
|
||||
std::string script_filename = "scripting.as";
|
||||
auto cached_script = m_loaded_files.find(script_filename);
|
||||
auto cached_function = m_functions_cache.find(function_name);
|
||||
|
||||
if (cached_script == m_loaded_files.end())
|
||||
{
|
||||
// Compile the script code
|
||||
Log::debug("Scripting", "Compiling script '%s' (was not in cache)", script_filename.c_str());
|
||||
r = compileScript(m_engine, script_filename);
|
||||
if (r < 0)
|
||||
{
|
||||
Log::debug("Scripting", "Script '%s' is not available", script_filename.c_str());
|
||||
m_loaded_files[script_filename] = false;
|
||||
m_functions_cache[function_name] = NULL; // remember that this script is unavailable
|
||||
return;
|
||||
}
|
||||
|
||||
m_loaded_files[script_filename] = true;
|
||||
}
|
||||
else if (cached_script->second == false)
|
||||
{
|
||||
return; // script file unavailable
|
||||
}
|
||||
|
||||
if (cached_function == m_functions_cache.end())
|
||||
{
|
||||
// Find the function for the function we want to execute.
|
||||
// This is how you call a normal function with arguments
|
||||
// asIScriptFunction *func = engine->GetModule(0)->GetFunctionByDecl("void func(arg1Type, arg2Type)");
|
||||
func = m_engine->GetModule(MODULE_ID_MAIN_SCRIPT_FILE)
|
||||
->GetFunctionByDecl(function_name.c_str());
|
||||
|
||||
if (func == NULL)
|
||||
{
|
||||
Log::debug("Scripting", "Scripting function was not found : %s", function_name.c_str());
|
||||
m_loaded_files[function_name] = NULL; // remember that this script is unavailable
|
||||
return;
|
||||
}
|
||||
|
||||
m_functions_cache[function_name] = func;
|
||||
func->AddRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r);
|
||||
// Script present in cache
|
||||
func = cached_function->second;
|
||||
}
|
||||
|
||||
if (func == NULL)
|
||||
{
|
||||
return; // function unavailable
|
||||
}
|
||||
|
||||
// Create a context that will execute the script.
|
||||
asIScriptContext *ctx = m_engine->CreateContext();
|
||||
if (ctx == NULL)
|
||||
{
|
||||
Log::error("Scripting", "Failed to create the context.");
|
||||
//m_engine->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare the script context with the function we wish to execute. Prepare()
|
||||
// must be called on the context before each new script function that will be
|
||||
// executed. Note, that if because we intend to execute the same function
|
||||
// several times, we will store the function returned by
|
||||
// GetFunctionByDecl(), so that this relatively slow call can be skipped.
|
||||
r = ctx->Prepare(func);
|
||||
if (r < 0)
|
||||
{
|
||||
Log::error("Scripting", "Failed to prepare the context.");
|
||||
ctx->Release();
|
||||
//m_engine->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Here, we can pass parameters to the script functions.
|
||||
//ctx->setArgType(index, value);
|
||||
//for example : ctx->SetArgFloat(0, 3.14159265359f);
|
||||
|
||||
if (callback)
|
||||
callback(ctx);
|
||||
|
||||
// Execute the function
|
||||
r = ctx->Execute();
|
||||
if (r != asEXECUTION_FINISHED)
|
||||
{
|
||||
// The execution didn't finish as we had planned. Determine why.
|
||||
if (r == asEXECUTION_ABORTED)
|
||||
{
|
||||
Log::error("Scripting", "The script was aborted before it could finish. Probably it timed out.");
|
||||
}
|
||||
else if (r == asEXECUTION_EXCEPTION)
|
||||
{
|
||||
Log::error("Scripting", "The script ended with an exception.");
|
||||
|
||||
// Write some information about the script exception
|
||||
asIScriptFunction *func = ctx->GetExceptionFunction();
|
||||
//std::cout << "func: " << func->GetDeclaration() << std::endl;
|
||||
//std::cout << "modl: " << func->GetModuleName() << std::endl;
|
||||
//std::cout << "sect: " << func->GetScriptSectionName() << std::endl;
|
||||
//std::cout << "line: " << ctx->GetExceptionLineNumber() << std::endl;
|
||||
//std::cout << "desc: " << ctx->GetExceptionString() << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Retrieve the return value from the context here (for scripts that return values)
|
||||
// <type> returnValue = ctx->getReturnType(); for example
|
||||
//float returnValue = ctx->GetReturnFloat();
|
||||
}
|
||||
|
||||
// We must release the contexts when no longer using them
|
||||
ctx->Release();
|
||||
}
|
||||
else
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ScriptEngine::cleanupCache()
|
||||
{
|
||||
// Retrieve the return value from the context here (for scripts that return values)
|
||||
// <type> returnValue = ctx->getReturnType(); for example
|
||||
//float returnValue = ctx->GetReturnFloat();
|
||||
for (auto curr : m_functions_cache)
|
||||
{
|
||||
if (curr.second != NULL)
|
||||
curr.second->Release();
|
||||
}
|
||||
m_functions_cache.clear();
|
||||
m_loaded_files.clear();
|
||||
}
|
||||
|
||||
// We must release the contexts when no longer using them
|
||||
ctx->Release();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
asIScriptFunction* ScriptEngine::registerScriptCallbacks(asIScriptEngine *engine,
|
||||
std::string function_name)
|
||||
{
|
||||
asIScriptFunction *func;
|
||||
func = engine->GetModule(0)->GetFunctionByDecl(function_name.c_str());
|
||||
return func;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ScriptEngine::cleanupCache()
|
||||
{
|
||||
for (auto curr : m_script_cache)
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Configures the script engine by binding functions, enums
|
||||
* \param asIScriptEngine engine = engine to configure
|
||||
*/
|
||||
void ScriptEngine::configureEngine(asIScriptEngine *engine)
|
||||
{
|
||||
if (curr.second != NULL)
|
||||
curr.second->Release();
|
||||
}
|
||||
m_script_cache.clear();
|
||||
}
|
||||
// Register the script string type
|
||||
RegisterStdString(engine); //register std::string
|
||||
RegisterVec3(engine); //register Vec3
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Configures the script engine by binding functions, enums
|
||||
* \param asIScriptEngine engine = engine to configure
|
||||
*/
|
||||
void ScriptEngine::configureEngine(asIScriptEngine *engine)
|
||||
{
|
||||
// Register the script string type
|
||||
RegisterStdString(engine); //register std::string
|
||||
RegisterVec3(engine); //register Vec3
|
||||
|
||||
Scripting::Track::registerScriptFunctions(m_engine);
|
||||
Scripting::Kart::registerScriptFunctions(m_engine);
|
||||
Scripting::Physics::registerScriptFunctions(m_engine);
|
||||
Scripting::Utils::registerScriptFunctions(m_engine);
|
||||
Scripting::GUI::registerScriptFunctions(m_engine);
|
||||
Scripting::GUI::registerScriptEnums(m_engine);
|
||||
Scripting::Track::registerScriptFunctions(m_engine);
|
||||
Scripting::Kart::registerScriptFunctions(m_engine);
|
||||
Scripting::Physics::registerScriptFunctions(m_engine);
|
||||
Scripting::Utils::registerScriptFunctions(m_engine);
|
||||
Scripting::GUI::registerScriptFunctions(m_engine);
|
||||
Scripting::GUI::registerScriptEnums(m_engine);
|
||||
|
||||
// It is possible to register the functions, properties, and types in
|
||||
// configuration groups as well. When compiling the scripts it can then
|
||||
// be defined which configuration groups should be available for that
|
||||
// script. If necessary a configuration group can also be removed from
|
||||
// the engine, so that the engine configuration could be changed
|
||||
// without having to recompile all the scripts.
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
int ScriptEngine::compileScript(asIScriptEngine *engine, std::string scriptName)
|
||||
{
|
||||
int r;
|
||||
|
||||
std::string script = getScript(scriptName);
|
||||
if (script.size() == 0)
|
||||
{
|
||||
// No such file
|
||||
return -1;
|
||||
// It is possible to register the functions, properties, and types in
|
||||
// configuration groups as well. When compiling the scripts it can then
|
||||
// be defined which configuration groups should be available for that
|
||||
// script. If necessary a configuration group can also be removed from
|
||||
// the engine, so that the engine configuration could be changed
|
||||
// without having to recompile all the scripts.
|
||||
}
|
||||
|
||||
// Add the script sections that will be compiled into executable code.
|
||||
// If we want to combine more than one file into the same script, then
|
||||
// we can call AddScriptSection() several times for the same module and
|
||||
// the script engine will treat them all as if they were one. The script
|
||||
// section name, will allow us to localize any errors in the script code.
|
||||
asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE);
|
||||
r = mod->AddScriptSection("script", &script[0], script.size());
|
||||
if (r < 0)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
int ScriptEngine::compileScript(asIScriptEngine *engine, std::string scriptName)
|
||||
{
|
||||
Log::error("Scripting", "AddScriptSection() failed");
|
||||
return -1;
|
||||
}
|
||||
int r;
|
||||
|
||||
std::string script = getScript(scriptName);
|
||||
if (script.size() == 0)
|
||||
{
|
||||
// No such file
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Add the script sections that will be compiled into executable code.
|
||||
// If we want to combine more than one file into the same script, then
|
||||
// we can call AddScriptSection() several times for the same module and
|
||||
// the script engine will treat them all as if they were one. The script
|
||||
// section name, will allow us to localize any errors in the script code.
|
||||
asIScriptModule *mod = engine->GetModule(MODULE_ID_MAIN_SCRIPT_FILE, asGM_ALWAYS_CREATE);
|
||||
r = mod->AddScriptSection("script", &script[0], script.size());
|
||||
if (r < 0)
|
||||
{
|
||||
Log::error("Scripting", "AddScriptSection() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Compile the script. If there are any compiler messages they will
|
||||
// be written to the message stream that we set right after creating the
|
||||
// script engine. If there are no errors, and no warnings, nothing will
|
||||
// be written to the stream.
|
||||
r = mod->Build();
|
||||
if (r < 0)
|
||||
{
|
||||
Log::error("Scripting", "Build() failed");
|
||||
return -1;
|
||||
// Compile the script. If there are any compiler messages they will
|
||||
// be written to the message stream that we set right after creating the
|
||||
// script engine. If there are no errors, and no warnings, nothing will
|
||||
// be written to the stream.
|
||||
r = mod->Build();
|
||||
if (r < 0)
|
||||
{
|
||||
Log::error("Scripting", "Build() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The engine doesn't keep a copy of the script sections after Build() has
|
||||
// returned. So if the script needs to be recompiled, then all the script
|
||||
// sections must be added again.
|
||||
|
||||
// If we want to have several scripts executing at different times but
|
||||
// that have no direct relation with each other, then we can compile them
|
||||
// into separate script modules. Each module uses their own namespace and
|
||||
// scope, so function names, and global variables will not conflict with
|
||||
// each other.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The engine doesn't keep a copy of the script sections after Build() has
|
||||
// returned. So if the script needs to be recompiled, then all the script
|
||||
// sections must be added again.
|
||||
|
||||
// If we want to have several scripts executing at different times but
|
||||
// that have no direct relation with each other, then we can compile them
|
||||
// into separate script modules. Each module uses their own namespace and
|
||||
// scope, so function names, and global variables will not conflict with
|
||||
// each other.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -51,16 +51,16 @@ namespace Scripting
|
||||
|
||||
ScriptEngine();
|
||||
~ScriptEngine();
|
||||
|
||||
void runScript(std::string scriptName);
|
||||
void runScript(std::string scriptName, std::function<void(asIScriptContext*)> callback);
|
||||
|
||||
void runFunction(std::string function_name);
|
||||
void runFunction(std::string function_name, std::function<void(asIScriptContext*)> callback);
|
||||
void evalScript(std::string script_fragment);
|
||||
void cleanupCache();
|
||||
asIScriptFunction*
|
||||
registerScriptCallbacks(asIScriptEngine *engine, std::string scriptName);
|
||||
|
||||
private:
|
||||
asIScriptEngine *m_engine;
|
||||
std::map<std::string, asIScriptFunction*> m_script_cache;
|
||||
std::map<std::string, bool> m_loaded_files;
|
||||
std::map<std::string, asIScriptFunction*> m_functions_cache;
|
||||
|
||||
void configureEngine(asIScriptEngine *engine);
|
||||
int compileScript(asIScriptEngine *engine,std::string scriptName);
|
||||
|
@ -115,7 +115,7 @@ namespace Scripting
|
||||
|
||||
void registerScriptFunctions(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
int r; // of type asERetCodes
|
||||
engine->SetDefaultNamespace("Kart");
|
||||
r = engine->RegisterGlobalFunction("void squash(int id, float time)", asFUNCTION(squash), asCALL_CDECL); assert(r >= 0);
|
||||
r = engine->RegisterGlobalFunction("void teleport(int id, const Vec3 &in)", asFUNCTION(teleport), asCALL_CDECL); assert(r >= 0);
|
||||
|
@ -227,7 +227,7 @@ namespace Scripting
|
||||
|
||||
void registerScriptFunctions(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
int r; // of type asERetCodes
|
||||
|
||||
engine->SetDefaultNamespace("Track");
|
||||
|
||||
@ -243,7 +243,7 @@ namespace Scripting
|
||||
r = engine->RegisterGlobalFunction("void enableTrigger(const string &in)", asFUNCTION(enableTrigger), asCALL_CDECL); assert(r >= 0);
|
||||
r = engine->RegisterGlobalFunction("void disableTrigger(const string &in)", asFUNCTION(disableTrigger), asCALL_CDECL); assert(r >= 0);
|
||||
r = engine->RegisterGlobalFunction("void createTrigger(const string &in, const Vec3 &in, float distance)",
|
||||
asFUNCTION(createTrigger), asCALL_GENERIC); assert(r >= 0);
|
||||
asFUNCTION(createTrigger), asCALL_CDECL); assert(r >= 0);
|
||||
r = engine->RegisterGlobalFunction("TrackObject@ getTrackObject(const string &in)", asFUNCTION(getTrackObject), asCALL_CDECL); assert(r >= 0);
|
||||
|
||||
// TrackObject
|
||||
|
@ -107,7 +107,7 @@ namespace Scripting
|
||||
{
|
||||
std::string *str = (std::string*)gen->GetArgAddress(0);
|
||||
ScriptEngine* script_engine = World::getWorld()->getScriptEngine();
|
||||
script_engine->runScript(*str);
|
||||
script_engine->runFunction(*str);
|
||||
}
|
||||
|
||||
/** Log to the console */
|
||||
|
86
src/states_screens/dialogs/scripting_console.cpp
Normal file
86
src/states_screens/dialogs/scripting_console.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2014-2015 Marc Coll
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "states_screens/dialogs/scripting_console.hpp"
|
||||
|
||||
#include "guiengine/engine.hpp"
|
||||
#include "guiengine/widgets/button_widget.hpp"
|
||||
#include "guiengine/widgets/label_widget.hpp"
|
||||
#include "guiengine/widgets/text_box_widget.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "scriptengine/script_engine.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
#include "utils/translation.hpp"
|
||||
|
||||
#include <IGUIEnvironment.h>
|
||||
|
||||
|
||||
using namespace GUIEngine;
|
||||
using namespace irr::core;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ScriptingConsole::ScriptingConsole() :
|
||||
ModalDialog(0.95f, 0.2f, GUIEngine::MODAL_DIALOG_LOCATION_BOTTOM)
|
||||
{
|
||||
m_fade_background = false;
|
||||
loadFromFile("scripting_console.stkgui");
|
||||
|
||||
TextBoxWidget* textCtrl = getWidget<TextBoxWidget>("textfield");
|
||||
assert(textCtrl != NULL);
|
||||
textCtrl->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ScriptingConsole::~ScriptingConsole()
|
||||
{
|
||||
// FIXME: what is this code for?
|
||||
TextBoxWidget* textCtrl = getWidget<TextBoxWidget>("textfield");
|
||||
textCtrl->getIrrlichtElement()->remove();
|
||||
textCtrl->clearListeners();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
GUIEngine::EventPropagation ScriptingConsole::processEvent(const std::string& eventSource)
|
||||
{
|
||||
if (eventSource == "close")
|
||||
{
|
||||
dismiss();
|
||||
return GUIEngine::EVENT_BLOCK;
|
||||
}
|
||||
else if (eventSource == "run")
|
||||
{
|
||||
runScript();
|
||||
return GUIEngine::EVENT_BLOCK;
|
||||
}
|
||||
return GUIEngine::EVENT_LET;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ScriptingConsole::runScript()
|
||||
{
|
||||
TextBoxWidget* textCtrl = getWidget<TextBoxWidget>("textfield");
|
||||
core::stringw script = textCtrl->getText();
|
||||
textCtrl->setText(L"");
|
||||
|
||||
World::getWorld()->getScriptEngine()->evalScript(core::stringc(script.c_str()).c_str());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
51
src/states_screens/dialogs/scripting_console.hpp
Normal file
51
src/states_screens/dialogs/scripting_console.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2014-2015 Marc Coll
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
|
||||
#ifndef HEADER_SCRIPTING_CONSOLE_DIALOG_HPP
|
||||
#define HEADER_SCRIPTING_CONSOLE_DIALOG_HPP
|
||||
|
||||
#include "guiengine/modaldialog.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
#include <irrString.h>
|
||||
|
||||
|
||||
namespace GUIEngine
|
||||
{
|
||||
class TextBoxWidget;
|
||||
class ButtonWidget;
|
||||
class LabelWidget;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Dialog that allows the player to enter the name for a new grand prix
|
||||
* \ingroup states_screens
|
||||
*/
|
||||
class ScriptingConsole : public GUIEngine::ModalDialog
|
||||
{
|
||||
public:
|
||||
|
||||
ScriptingConsole();
|
||||
~ScriptingConsole();
|
||||
|
||||
virtual void onEnterPressedInternal() OVERRIDE{ runScript(); }
|
||||
void runScript();
|
||||
GUIEngine::EventPropagation processEvent(const std::string& eventSource);
|
||||
};
|
||||
|
||||
#endif
|
@ -1486,7 +1486,7 @@ void Track::update(float dt)
|
||||
if (!m_startup_run) // first time running update = good point to run startup script
|
||||
{
|
||||
Scripting::ScriptEngine* script_engine = World::getWorld()->getScriptEngine();
|
||||
script_engine->runScript("void onStart()");
|
||||
script_engine->runFunction("void onStart()");
|
||||
m_startup_run = true;
|
||||
}
|
||||
m_track_object_manager->update(dt);
|
||||
|
@ -955,7 +955,7 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who)
|
||||
Camera* camera = Camera::getActiveCamera();
|
||||
if (camera != NULL && camera->getKart() != NULL)
|
||||
idKart = camera->getKart()->getWorldKartId();
|
||||
script_engine->runScript("void " + m_action + "(int)",
|
||||
script_engine->runFunction("void " + m_action + "(int)",
|
||||
[=](asIScriptContext* ctx) { ctx->SetArgDWord(0, idKart); });
|
||||
}
|
||||
} // onTriggerItemApproached
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "main_loop.hpp"
|
||||
#include "replay/replay_recorder.hpp"
|
||||
#include "states_screens/dialogs/debug_slider.hpp"
|
||||
#include "states_screens/dialogs/scripting_console.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/profiler.hpp"
|
||||
@ -103,6 +104,7 @@ enum DebugMenuCommand
|
||||
DEBUG_VISUAL_VALUES,
|
||||
DEBUG_PRINT_START_POS,
|
||||
DEBUG_ADJUST_LIGHTS,
|
||||
DEBUG_SCRIPT_CONSOLE
|
||||
}; // DebugMenuCommand
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -265,6 +267,7 @@ bool onEvent(const SEvent &event)
|
||||
mnu->addItem(L"Save history", DEBUG_SAVE_HISTORY);
|
||||
mnu->addItem(L"Print position", DEBUG_PRINT_START_POS);
|
||||
mnu->addItem(L"Adjust Lights", DEBUG_ADJUST_LIGHTS);
|
||||
mnu->addItem(L"Scripting console", DEBUG_SCRIPT_CONSOLE);
|
||||
|
||||
g_debug_menu_visible = true;
|
||||
irr_driver->showPointer();
|
||||
@ -650,6 +653,10 @@ bool onEvent(const SEvent &event)
|
||||
dsd->changeLabel("SSAO Sigma", "[None]");
|
||||
#endif
|
||||
}
|
||||
else if (cmdID == DEBUG_SCRIPT_CONSOLE)
|
||||
{
|
||||
ScriptingConsole* console = new ScriptingConsole();
|
||||
}
|
||||
}
|
||||
|
||||
return false; // event has been handled
|
||||
|
Loading…
x
Reference in New Issue
Block a user