From 15b35b76ace0df45ce3f6e601200f0cb28b03c62 Mon Sep 17 00:00:00 2001 From: Marianne Gagnon Date: Sat, 2 Jan 2016 20:34:39 -0500 Subject: [PATCH] Scripting : add ability to use delegates for timeouts (enabling complex timeouts) --- src/scriptengine/script_engine.cpp | 134 ++++++++++++++++++++++++++++- src/scriptengine/script_engine.hpp | 29 ++++++- src/scriptengine/script_utils.cpp | 12 ++- 3 files changed, 167 insertions(+), 8 deletions(-) diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 3db73c2b8..5719b7dde 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -172,6 +172,99 @@ namespace Scripting //----------------------------------------------------------------------------- + void ScriptEngine::runDelegate(asIScriptFunction* delegate) + { + asIScriptContext *ctx = m_engine->CreateContext(); + if (ctx == NULL) + { + Log::error("Scripting", "runMethod: Failed to create the context."); + //m_engine->Release(); + return; + } + + int r = ctx->Prepare(delegate); + if (r < 0) + { + Log::error("Scripting", "runMethod: Failed to prepare the context."); + ctx->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 : (line %i) %s", + ctx->GetExceptionLineNumber(), + ctx->GetExceptionString()); + } + else + { + Log::error("Scripting", "The script ended for some unforeseen reason (%i)", r); + } + } + + ctx->Release(); + } + + //----------------------------------------------------------------------------- + + /* + void ScriptEngine::runMethod(asIScriptObject* obj, std::string methodName) + { + asIObjectType* type = obj->GetObjectType(); + asIScriptFunction* method = type->GetMethodByName(methodName.c_str()); + if (method == NULL) + Log::error("Scripting", ("runMethod: object does not implement method " + methodName).c_str()); + + + asIScriptContext *ctx = m_engine->CreateContext(); + if (ctx == NULL) + { + Log::error("Scripting", "runMethod: Failed to create the context."); + //m_engine->Release(); + return; + } + + int r = ctx->Prepare(method); + if (r < 0) + { + Log::error("Scripting", "runMethod: Failed to prepare the context."); + ctx->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); + } + } + + ctx->Release(); + } + */ + //----------------------------------------------------------------------------- + /** runs the specified script * \param string scriptName = name of script to run */ @@ -415,9 +508,36 @@ namespace Scripting //----------------------------------------------------------------------------- + PendingTimeout::PendingTimeout(double time, asIScriptFunction* callback_delegate) + : PendingTimeout() + { + m_time = time; + m_callback_delegate = callback_delegate; + } + + //----------------------------------------------------------------------------- + + PendingTimeout::~PendingTimeout() + { + if (m_callback_delegate != NULL) + { + asIScriptEngine* engine = World::getWorld()->getScriptEngine()->getEngine(); + m_callback_delegate->Release(); + } + } + + //----------------------------------------------------------------------------- + void ScriptEngine::addPendingTimeout(double time, const std::string& callback_name) { - m_pending_timeouts.push_back(PendingTimeout(time, callback_name)); + m_pending_timeouts.push_back(new PendingTimeout(time, callback_name)); + } + + //----------------------------------------------------------------------------- + + void ScriptEngine::addPendingTimeout(double time, asIScriptFunction* delegate) + { + m_pending_timeouts.push_back(new PendingTimeout(time, delegate)); } //----------------------------------------------------------------------------- @@ -430,8 +550,16 @@ namespace Scripting curr.m_time -= dt; if (curr.m_time <= 0.0) { - runFunction(true, "void " + curr.m_callback_name + "()"); - m_pending_timeouts.erase(m_pending_timeouts.begin() + i); + if (curr.m_callback_delegate != NULL) + { + runDelegate(curr.m_callback_delegate); + } + else + { + runFunction(true, "void " + curr.m_callback_name + "()"); + } + + m_pending_timeouts.erase(i); } } } diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index 4cc4f95fe..1a412addb 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -23,21 +23,38 @@ #include #include +#include "scriptengine/script_utils.hpp" +#include "utils/ptr_vector.hpp" + class TrackObjectPresentation; namespace Scripting { /** Represents a scripting function to execute after a given time */ - struct PendingTimeout + struct PendingTimeout : NoCopy { double m_time; - std::string m_callback_name; - PendingTimeout(double time, const std::string& callback_name) + /** We have two callback types: a string containing the name of the function + * to call (simple callback) or a "TimeoutBase" object (advanced callback) + */ + std::string m_callback_name; + asIScriptFunction* m_callback_delegate; + + PendingTimeout() + { + m_callback_delegate = NULL; + } + + PendingTimeout(double time, const std::string& callback_name) : PendingTimeout() { m_time = time; m_callback_name = callback_name; } + + PendingTimeout(double time, asIScriptFunction* callback_delegate); + + ~PendingTimeout(); }; class ScriptEngine @@ -53,6 +70,7 @@ namespace Scripting void runFunction(bool warn_if_not_found, std::string function_name, std::function callback, std::function get_return_value); + void runDelegate(asIScriptFunction* delegate_fn); void evalScript(std::string script_fragment); void cleanupCache(); @@ -60,12 +78,15 @@ namespace Scripting bool compileLoadedScripts(); void addPendingTimeout(double time, const std::string& callback_name); + void addPendingTimeout(double time, asIScriptFunction* delegate_fn); void update(double dt); + asIScriptEngine* getEngine() { return m_engine; } + private: asIScriptEngine *m_engine; std::map m_functions_cache; - std::vector m_pending_timeouts; + PtrVector m_pending_timeouts; void configureEngine(asIScriptEngine *engine); }; // class ScriptEngine diff --git a/src/scriptengine/script_utils.cpp b/src/scriptengine/script_utils.cpp index c824e0e30..be5c80d2e 100644 --- a/src/scriptengine/script_utils.cpp +++ b/src/scriptengine/script_utils.cpp @@ -16,7 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "script_track.hpp" +#include "script_utils.hpp" #include "animations/three_d_animation.hpp" #include "input/device_manager.hpp" @@ -132,6 +132,12 @@ namespace Scripting World::getWorld()->getScriptEngine()->addPendingTimeout(delay, *callback_name); } + /** Call a method from the given object after the specified delay */ + void setTimeoutDelegate(asIScriptFunction* obj, float delay) + { + World::getWorld()->getScriptEngine()->addPendingTimeout(delay, obj); + } + /** Log to the console */ void logInfo(std::string* log) { @@ -180,6 +186,7 @@ namespace Scripting { int r; // of type asERetCodes engine->SetDefaultNamespace("Utils"); + r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in)", asFUNCTION(proxy_insertValues1), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues2), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("string insertValues(const string &in, const string &in, const string &in, const string &in)", asFUNCTION(proxy_insertValues3), asCALL_CDECL); assert(r >= 0); @@ -190,6 +197,9 @@ namespace Scripting r = engine->RegisterGlobalFunction("int randomInt(int, int)", asFUNCTION(randomInt), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("float randomFloat(int, int)", asFUNCTION(randomFloat), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void setTimeout(const string &in, float)", asFUNCTION(setTimeout), asCALL_CDECL); assert(r >= 0); + + r = engine->RegisterFuncdef("void TimeoutCallback()"); assert(r >= 0); + r = engine->RegisterGlobalFunction("void setTimeoutDelegate(TimeoutCallback@, float)", asFUNCTION(setTimeoutDelegate), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void logInfo(const string &in)", asFUNCTION(logInfo), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void logWarning(const string &in)", asFUNCTION(logWarning), asCALL_CDECL); assert(r >= 0);