diff --git a/resources/scripts/example_angelscript_linecallback.as b/resources/scripts/example_angelscript_linecallback.as new file mode 100644 index 0000000000..f25a4a6644 --- /dev/null +++ b/resources/scripts/example_angelscript_linecallback.as @@ -0,0 +1,62 @@ +// TEST SCRIPT - angelscript linecallback (tracks executed lines) +// =================================================== + +// GUI (intentionally at the top to have fewer lines in the output): +void frameStep(float dt) +{ + ImGui::TextDisabled("Frames:"+framecounter+", Samples: "+totalSamples+"(per frame :"+linenumsLite.length()+')'); + ImGui::Text("[Line] Total"); + + for (uint i=0; i linenumsLite; +array lineHitsAggregatedLite; +int framecounter = 0; + +// SETUP: +void main(){ + // this automagically attaches the LineCallback to angelscript. + game.registerForEvent(SE_ANGELSCRIPT_LINECALLBACK); +} + +// EVENT HANDLING: +void eventCallbackEx(scriptEvents ev, int a1, int a2, int a3, int a4, string a5, string a6, string a7, string a8) +{ + if (ev == SE_ANGELSCRIPT_LINECALLBACK) { addSample(a2); } +} + +// HELPERS: +void addSample(int a2) +{ + if (a2 < lowestLinenum) { lowestLinenum=a2; } + else if (a2 > highestLinenum) { highestLinenum = a2; } + // detect repeating + if (a2 < highestLinenum && a2 == lowestLinenum) { aggregateLineHitsLite(); } + linenumsLite.insertLast(a2); + totalSamples++; +} + +void aggregateLineHitsLite() +{ + // allocate + lineHitsAggregatedLite.resize(highestLinenum+1); + + // aggregate + for (uint i=0; isection, msg->message); // strings } +std::string ptr2str(const char* ptr) { if (ptr) return ptr; else return ""; } + +void ScriptEngine::lineCallback(AngelScript::asIScriptContext* ctx) +{ + std::string funcName, funcObjTypeName, objName; + if (ctx->GetFunction()) + { + funcName = ptr2str(ctx->GetFunction()->GetName()); + objName = ptr2str(ctx->GetFunction()->GetObjectName()); + if (ctx->GetFunction()->GetObjectType()) + funcObjTypeName = ptr2str(ctx->GetFunction()->GetObjectType()->GetName()); + } + TRIGGER_EVENT_ASYNC(SE_ANGELSCRIPT_LINECALLBACK, + // ints + m_currently_executing_script_unit, ctx->GetLineNumber(), ctx->GetCallstackSize(), 0, + // strings + funcName, funcObjTypeName, objName + ); +} + int ScriptEngine::framestep(Real dt) { // Check if we need to execute any strings @@ -221,6 +241,10 @@ int ScriptEngine::framestep(Real dt) context->Prepare(m_script_units[id].frameStepFunctionPtr); + // Automatically attach the LineCallback if the script registered for the event. + if (m_script_units[id].eventMask & SE_ANGELSCRIPT_LINECALLBACK) + context->SetLineCallback(asMETHOD(ScriptEngine, lineCallback), this, asCALL_THISCALL); + // Set the function arguments context->SetArgFloat(0, dt); @@ -232,6 +256,9 @@ int ScriptEngine::framestep(Real dt) // The return value is only valid if the execution finished successfully AngelScript::asDWORD ret = context->GetReturnDWord(); } + + // Clear the line callback so it doesn't intercept game callbacks (causes infinite loop). + context->ClearLineCallback(); } return 0; } diff --git a/source/main/scripting/ScriptEngine.h b/source/main/scripting/ScriptEngine.h index 7b36732fb6..c0ab4c5925 100644 --- a/source/main/scripting/ScriptEngine.h +++ b/source/main/scripting/ScriptEngine.h @@ -222,6 +222,12 @@ class ScriptEngine : public Ogre::LogListener */ void msgCallback(const AngelScript::asSMessageInfo* msg); + /** + * Optional callback which receives diagnostic info for every executed statement. + * https://www.angelcode.com/angelscript/sdk/docs/manual/classas_i_script_context.html#ae2747f643bf9a07364f922c460ef57dd + */ + void lineCallback(AngelScript::asIScriptContext* ctx); + Ogre::String composeModuleName(Ogre::String const& scriptName, ScriptCategory origin, ScriptUnitId_t id); /** diff --git a/source/main/scripting/bindings/ScriptEventsAngelscript.cpp b/source/main/scripting/bindings/ScriptEventsAngelscript.cpp index 392d160ef0..9fd86a53eb 100644 --- a/source/main/scripting/bindings/ScriptEventsAngelscript.cpp +++ b/source/main/scripting/bindings/ScriptEventsAngelscript.cpp @@ -57,6 +57,7 @@ void RoR::RegisterScriptEvents(asIScriptEngine *engine) result = engine->RegisterEnumValue("scriptEvents", "SE_ANGELSCRIPT_MANIPULATIONS", SE_ANGELSCRIPT_MANIPULATIONS); ROR_ASSERT(result>=0); result = engine->RegisterEnumValue("scriptEvents", "SE_ANGELSCRIPT_MSGCALLBACK", SE_ANGELSCRIPT_MSGCALLBACK); ROR_ASSERT(result>=0); + result = engine->RegisterEnumValue("scriptEvents", "SE_ANGELSCRIPT_LINECALLBACK", SE_ANGELSCRIPT_LINECALLBACK); ROR_ASSERT(result>=0); result = engine->RegisterEnumValue("scriptEvents", "SE_ANGELSCRIPT_THREAD_STATUS", SE_ANGELSCRIPT_THREAD_STATUS); ROR_ASSERT(result>=0); result = engine->RegisterEnumValue("scriptEvents", "SE_GENERIC_MESSAGEBOX_CLICK", SE_GENERIC_MESSAGEBOX_CLICK); ROR_ASSERT(result>=0);