From 8152e61db5ef7aaace1c964cf067a6d3f776afaa Mon Sep 17 00:00:00 2001 From: smoothchat <100860237+smoothchat@users.noreply.github.com> Date: Thu, 7 Aug 2025 18:11:09 +1000 Subject: [PATCH] An added callback which runs in AFTER flightloop phase of XP11 Added a callback that runs in the "AFTER" flightloop phase thereby not suffering graphical jitter as experienced by the do_every_frame() callback. This particularly assists lua code that manipulates AI Aircraft positions which currently exhibit significant jitter. --- src/FlyWithLua.cpp | 168 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/src/FlyWithLua.cpp b/src/FlyWithLua.cpp index 4ab5d9f..0d246ba 100644 --- a/src/FlyWithLua.cpp +++ b/src/FlyWithLua.cpp @@ -2,7 +2,7 @@ // FlyWithLua Plugin for X-Plane 11 // ---------------------------------- -#define PLUGIN_VERSION_NO "2.7.38" +#define PLUGIN_VERSION_NO "2.7.39" #define PLUGIN_VERSION_BUILD __DATE__ " " __TIME__ #define PLUGIN_VERSION PLUGIN_VERSION_NO " build " PLUGIN_VERSION_BUILD @@ -157,6 +157,9 @@ * Thanks osprey-12 * v2.7.37 [added] Added support for the horizontal scrollbar in Imgui windows. * v2.7.38 [added] Removed X-Plane LuaJIT alloc. + * v2.2.39 [Added] do_every_frame_after() callback to run in FlightLoop Phase 1 (after physics processing, thus eliminating + * graphic jitter + * [Added] do_every_frame_before() callback to run in FlightLoop Phase 1 (before physics processing) * * Markus (Teddii): * v2.1.20 [changed] bug fixed in Luahid_open() and Luahid_open_path(), setting last HID device index back if no device was found @@ -300,6 +303,7 @@ //#endif // include the extern command provided by the LUA team #include +using namespace flywithlua; // Resolve namespace issues when compiling /// This symbol comes from statically linked LuaXML_lib library. extern "C" int luaopen_LuaXML_lib(lua_State* L); @@ -2873,6 +2877,146 @@ static int LuaDoEveryFrame(lua_State* L) return 0; } +// --- MULTI-CALLBACK VERSION OF: FlightLoop Callback BeforeFlightModel --- + +static XPLMFlightLoopID g_DoEveryFrameBefore_ID = nullptr; +static std::vector do_every_frame_before_code; + +// Called every frame before flight model is updated +float Do_Every_Frame_Before(float inElapsedSinceLastCall, float inElapsedTimeSinceLastFlightLoop, + int inCounter, void* inRefcon) +{ + if (!LuaIsRunning) return -1.0f; + + CopyDataRefsToLua(); + + for (const auto& code : do_every_frame_before_code) + { + if (luaL_loadstring(FWLLua, code.c_str()) == LUA_OK) + { + if (lua_pcall(FWLLua, 0, 0, 0) != LUA_OK) + { + const char* err = lua_tostring(FWLLua, -1); + logMsg(logToDevCon, std::string("Error in do_every_frame_before(): ") + err); + lua_pop(FWLLua, 1); + } + } + else + { + const char* err = lua_tostring(FWLLua, -1); + logMsg(logToDevCon, std::string("Syntax error in do_every_frame_before(): ") + err); + lua_pop(FWLLua, 1); + } + } + + CopyDataRefsToXPlane(); + + return -1.0f; // every frame +} + +void Register_Do_Every_Frame_Before() +{ + if (g_DoEveryFrameBefore_ID != nullptr) + return; + + XPLMCreateFlightLoop_t loop_params{}; + loop_params.structSize = sizeof(XPLMCreateFlightLoop_t); + loop_params.phase = xplm_FlightLoop_Phase_BeforeFlightModel; + loop_params.callbackFunc = Do_Every_Frame_Before; + loop_params.refcon = nullptr; + + g_DoEveryFrameBefore_ID = XPLMCreateFlightLoop(&loop_params); + XPLMScheduleFlightLoop(g_DoEveryFrameBefore_ID, -1.0, 0); +} + +static int LuaDoEveryFrameBefore(lua_State* L) +{ + if (!lua_isstring(L, 1)) + { + logMsg(logToDevCon, "FlyWithLua Error: do_every_frame_before() needs a string of Lua code."); + LuaIsRunning = false; + return 0; + } + + std::string lua_code = lua_tostring(L, 1); + do_every_frame_before_code.push_back(lua_code); + + return 0; +} + +// --- End DoEveryFrameBefore routines + + +// --- MULTI-CALLBACK VERSION OF: FlightLoop Callback AfterFlightModel --- + +static XPLMFlightLoopID g_DoEveryFrameAfter_ID = nullptr; +static std::vector do_every_frame_after_code; + +// Called every frame after flight model is updated +float Do_Every_Frame_After(float inElapsedSinceLastCall, float inElapsedTimeSinceLastFlightLoop, + int inCounter, void* inRefcon) +{ + if (!LuaIsRunning) return -1.0f; + + CopyDataRefsToLua(); + + for (const auto& code : do_every_frame_after_code) + { + if (luaL_loadstring(FWLLua, code.c_str()) == LUA_OK) + { + if (lua_pcall(FWLLua, 0, 0, 0) != LUA_OK) + { + const char* err = lua_tostring(FWLLua, -1); + logMsg(logToDevCon, std::string("Error in do_every_frame_after(): ") + err); + lua_pop(FWLLua, 1); + } + } + else + { + const char* err = lua_tostring(FWLLua, -1); + logMsg(logToDevCon, std::string("Syntax error in do_every_frame_after(): ") + err); + lua_pop(FWLLua, 1); + } + } + + CopyDataRefsToXPlane(); + + return -1.0f; // every frame +} + +void Register_Do_Every_Frame_After() +{ + if (g_DoEveryFrameAfter_ID != nullptr) + return; + + XPLMCreateFlightLoop_t loop_params{}; + loop_params.structSize = sizeof(XPLMCreateFlightLoop_t); + loop_params.phase = xplm_FlightLoop_Phase_AfterFlightModel; + loop_params.callbackFunc = Do_Every_Frame_After; + loop_params.refcon = nullptr; + + g_DoEveryFrameAfter_ID = XPLMCreateFlightLoop(&loop_params); + XPLMScheduleFlightLoop(g_DoEveryFrameAfter_ID, -1.0, 0); +} + +static int LuaDoEveryFrameAfter(lua_State* L) +{ + if (!lua_isstring(L, 1)) + { + logMsg(logToDevCon, "FlyWithLua Error: do_every_frame_after() needs a string of Lua code."); + LuaIsRunning = false; + return 0; + } + + std::string lua_code = lua_tostring(L, 1); + do_every_frame_after_code.push_back(lua_code); + + return 0; +} + +// --- End DoEveryFrameAfter routines + + static int LuaDoOften(lua_State* L) { if (!lua_isstring(L, 1)) @@ -5966,6 +6110,8 @@ void RegisterCoreCFunctionsToLua(lua_State* L) lua_register(L, "do_on_mouse_wheel", LuaDoEveryMouseWheel); lua_register(L, "do_every_draw", LuaDoEveryDrawCallback); lua_register(L, "do_every_frame", LuaDoEveryFrame); + lua_register(L, "do_every_frame_before", LuaDoEveryFrameBefore); // Callback before FlightModel + lua_register(L, "do_every_frame_after", LuaDoEveryFrameAfter); // Callback after FlightModel lua_register(L, "do_often", LuaDoOften); lua_register(L, "do_sometimes", LuaDoSometimes); lua_register(L, "add_macro", LuaAddMacro); @@ -7316,6 +7462,22 @@ PLUGIN_API void XPluginDisable(void) XPLMUnregisterFlightLoopCallback(MyFastLoopCallback, nullptr); XPLMUnregisterFlightLoopCallback(MySlowLoopCallback, nullptr); XPLMUnregisterFlightLoopCallback(MyEveryFrameLoopCallback, nullptr); + + // Unregister and clean up the BeforeFlightModel flight loop + if (g_DoEveryFrameBefore_ID != nullptr) + { + XPLMDestroyFlightLoop(g_DoEveryFrameBefore_ID); + g_DoEveryFrameBefore_ID = nullptr; + do_every_frame_before_code.clear(); + } + + // Unregister and clean up the AfterFlightModel flight loop + if (g_DoEveryFrameAfter_ID != nullptr) + { + XPLMDestroyFlightLoop(g_DoEveryFrameAfter_ID); + g_DoEveryFrameAfter_ID = nullptr; + do_every_frame_after_code.clear(); + } // write to Log.txt logMsg(logToDevCon, "FlyWithLua Info: FlyWithLua plugin disabled."); @@ -7371,6 +7533,10 @@ PLUGIN_API int XPluginEnable(void) nullptr); /* refcon not used. */ XPLMRegisterDrawCallback(FWLDrawWindowCallback, xplm_Phase_Window, 0, (void*) "FWLWindowDrawer"); + + Register_Do_Every_Frame_Before(); // Provide a callback that runs in FlightLoop Phase Before mode. + + Register_Do_Every_Frame_After(); // Provide a callback that runs in FlightLoop Phase After mode. // create the FlyWithLua menu inside the plugin menu if (FlyWithLuaMenuItem < 0)