
游戏开发逆向工程【免费下载链接】RE-UE4SSInjectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games项目地址https://gitcode.com/gh_mirrors/re/RE-UE4SS点击查看免费下载ExecuteWithDelay是 UE4SS Lua API 中用于异步延时执行回调的全局函数它接收一个以毫秒为单位的延迟时间与一个 Lua 回调函数在后台异步线程上等待指定时间后调用该回调而不会阻塞游戏的游戏线程Game Thread。本文以官方文档 executewithdelay.md 为主体结合 LuaMod.cpp 中的底层实现完整讲解其签名、调用方式、底层调度机制、与同类 API 的对比以及在新版 Delayed Action 系统中的迁移方案帮助你写出既安全又可控的定时逻辑。一、函数签名与参数说明ExecuteWithDelay是注入进每个 Lua Mod 全局命名空间的函数无需require即可直接调用。官方文档给出的签名与参数如下#类型说明1integer延迟毫秒数Delay in milliseconds即等待多久后执行回调2function回调函数Callback延迟结束后被异步执行参数顺序固定为「先延迟、后回调」与许多脚本语言中常见的setTimeout(delay, fn)一致。延迟以毫秒为单位1000代表 1 秒2000代表 2 秒以此类推。官方示例ExecuteWithDelay(2000, function() print(Executed asynchronously after a 2 second delay\n) end)这段代码注册了一个 2000ms2 秒后触发的回调回调内部向 UE4SS 控制台输出一行文本。调用ExecuteWithDelay本身会立即返回不会阻塞当前脚本的继续执行后续代码会马上接着运行。典型的组合用法顺序执行多段延迟逻辑由于调用本身不阻塞你可以在同一个 Mod 里按时间轴注册多段逻辑实现延时-执行-再延时-再执行的序列化流程print(T0ms: 脚本启动\n) ExecuteWithDelay(1000, function() print(T1000ms: 第一次延时回调\n) -- 在回调内部再次注册下一个延时 ExecuteWithDelay(1500, function() print(T2500ms: 第二次延时回调\n) end) end)在回调内再次调用ExecuteWithDelay即可实现链式定时这是编写 UI 提示淡出、阶段性任务脚本时的常用模式。二、参数校验与错误提示源码级的严格性从源码看ExecuteWithDelay的注册位于 LuaMod.cpp 的 L3065-L3094。它内部维护了一段用于报错的重载说明文本No overload found for function ExecuteWithDelay. Overloads: #1: ExecuteWithDelay(integer DelayInMilliseconds, LuaFunction Callback)参数校验过程分两步进行先校验延迟参数调用lua.is_integer()检查第一个参数是否为整数若否直接抛出std::runtime_error错误信息即上面的重载说明再校验回调参数调用lua.is_function()检查第二个参数是否为函数若否同样抛出异常。也就是说下面这些调用都会触发明确的错误提示而非静默失败-- 错误第一个参数不是整数 ExecuteWithDelay(2000, function() end) -- 错误第二个参数不是函数 ExecuteWithDelay(2000, not a function)注意延迟参数必须按整数传入浮点数如2000.5或字符串都不会通过lua.is_integer()的校验。因此在实践中建议使用整数常量或math.floor处理后的值。校验通过后源码通过lua.registry().make_ref()将回调函数登记进 Lua 注册表得到一个稳定的引用lua_function_ref防止回调在等待期间被 Lua 垃圾回收随后将回调引用、动作类型ActionType::Delayed、创建时间std::chrono::steady_clock::now()和延迟毫秒数一起封装成LuaMod::AsyncAction压入 Mod 的待处理队列m_pending_actions。三、底层执行机制专用异步线程与 5ms 轮询ExecuteWithDelay的异步属性并非靠游戏线程上的定时器实现而是由 UE4SS 为每个 Lua Mod 维护的专用异步线程驱动。理解这条链路你就明白了它为什么不会卡住游戏。1. 线程与 Lua 状态的创建Mod 启动流程中LuaMod.cpp L6000-L6003依次执行make_main_state(...)创建主 Lua 状态setup_lua_global_functions_main_state_only()注册仅在主状态可见的全局函数make_async_state(...)在 Lua 上创建新线程lua_newthread作为异步 Lua 状态并通过lua_setglobal(lua.get_lua_state(), AsyncThread)将其注册为全局变量AsyncThread防止被 GCstart_async_thread()启动专用的异步处理线程。也正因如此ExecuteWithDelay的回调运行在异步线程的 Lua 状态async_lua上而非游戏线程。C Mod 的on_lua_start等接口中传入的async_lua参数官方注释也明确写着它是用于 ExecuteAsync 和 ExecuteWithDelay 等异步场景的 Lua 实例见 CppUserModBase.hpp L74。2. 轮询调度process_delayed_actions异步线程的主循环是update_async()LuaMod.cpp L7222-L7235每轮执行process_delayed_actions()后sleep_for(5ms)即约每 5 毫秒轮询一次待处理动作队列。process_delayed_actions()LuaMod.cpp L7237-L7292的核心逻辑是通过actions_lock()加锁把m_pending_actions中的新动作整体搬移std::make_move_iterator到m_delayed_actions随后清空待处理队列并解锁——写入与调度使用互斥锁保护保证多线程安全遍历m_delayed_actions用steady_clock计算每个动作已流逝的毫秒数passed判断条件为action.type Immediate || passed action.delay对Delayed类型而言只有累计流逝时间大于等于所设延迟才满足触发条件满足条件的动作通过async_lua()-registry().get_function_ref(...)取回回调引用并call_function(0, 0)调用若回调抛出异常会被捕获并以[DelayedAction] 错误信息的形式输出到日志避免整个异步线程崩溃已触发或循环型返回停止的动作从队列中移除。由此可见ExecuteWithDelay的延迟是调度轮询下的近似延迟由于每 5ms 才检查一次回调实际触发时间约为「设定延迟 0~5ms 的调度误差」。对绝大多数游戏内脚本场景秒级延迟完全够用但不适合对毫秒级精度有严格要求的场合。3. 线程安全与隔离回调在每个 Mod 自己的异步线程上执行不同 Mod 的异步动作互不干扰队列写入注册时与消费轮询时通过actions_lock()互斥保护Mod 停止时代码会先请求停止异步线程并join()再清空队列clear_delayed_actions()LuaMod.cpp L7294-L7300避免悬空回调访问已释放的 Lua 状态。线程安全提示异步回调运行在非游戏线程回调内若需要操作游戏对象/属性应当通过ExecuteInGameThread等机制切回游戏线程否则可能引发 UE 的线程安全问题。可用 isingamethread.md 与 getasyncthreadid.md 中描述的IsInGameThread、GetAsyncThreadId等函数辅助判断当前上下文。四、与同类 API 的对比与选型1. ExecuteAsync无延迟的立即异步执行ExecuteAsync与ExecuteWithDelay的差异仅在于延迟。从源码看LuaMod.cpp L3044-L3063它同样把回调引用压入m_pending_actions但动作类型为ActionType::Immediate不记录延迟——在process_delayed_actions的触发条件中Immediate类型跳过延迟判断直接执行唯一成本是等待下一个 5ms 轮询周期。官方文档 executeasync.md 也明确指出它与ExecuteWithDelay工作方式类似只是除了注册回调的开销外没有额外延迟。ExecuteAsync(function() print(Executed asynchronously\n) end)2. LoopAsync周期循环执行LoopAsync(delayMs, callback)与ExecuteWithDelay共享同一套队列与轮询机制区别在于ActionType::Loop触发后若不返回false则重置created_at并留在队列中继续下一轮LuaMod.cpp L7261-L7266。返回true则表示停止动作被移除。3. 表格对比函数延迟触发方式可取消可暂停运行线程ExecuteWithDelay(delayMs, cb)毫秒级单次否否异步线程ExecuteAsync(cb)无约一个轮询周期单次否否异步线程LoopAsync(delayMs, cb)毫秒级循环否靠回调返回 false否异步线程ExecuteInGameThreadWithDelay(delayMs, cb)新版毫秒级单次是是游戏线程五、迁移建议新版 Delayed Action 系统ExecuteWithDelay目前仍然可用且未被弃用但官方推荐的新一代定时系统是Delayed Action 系统完整文档见 delayedactions.md。相比之下它提供了 UE 风格的全套控制能力可取消CancelDelayedAction(handle)可暂停/恢复PauseDelayedAction(handle)、UnpauseDelayedAction(handle)可重置/改时ResetDelayedActionTimer(handle)、SetDelayedActionTimer(handle, newDelayMs)可查询状态IsValidDelayedActionHandle、IsDelayedActionActive、IsDelayedActionPaused、GetDelayedActionTimeRemaining等支持帧数延迟ExecuteInGameThreadAfterFrames(frames, cb)需先检查EngineTickAvailable支持循环LoopInGameThreadWithDelay(delayMs, cb)其中与ExecuteWithDelay最直接对应的替代是ExecuteInGameThreadWithDelay-- 旧写法异步线程执行 ExecuteWithDelay(2000, function() print(2 秒后执行异步线程\n) end) -- 新写法游戏线程执行返回句柄 local handle ExecuteInGameThreadWithDelay(2000, function() print(2 秒后执行游戏线程\n) end) -- 之后可以随时取消 -- CancelDelayedAction(handle)迁移对照表详见 upgrade-guide.md 的 ExecuteAsync 与 LoopAsync 章节旧函数推荐新函数ExecuteWithDelay(delayMs, callback)ExecuteInGameThreadWithDelay(delayMs, callback)ExecuteAsync(callback)ExecuteInGameThreadWithDelay(0, callback)LoopAsync(delayMs, callback)LoopInGameThreadWithDelay(delayMs, callback)迁移决策参考若你需要取消、暂停或查询定时任务例如技能冷却、可打断的倒计时请直接使用 Delayed Action 系统若你只是需要一段到时即执行、不关心控制的一次性逻辑ExecuteWithDelay依然可以正常工作注意新旧系统的线程差异ExecuteWithDelay的回调跑在异步线程ExecuteInGameThreadWithDelay的回调跑在游戏线程。若回调需要直接操作 UE 对象属性新版反而更安全无需手动切线程。六、相关资源函数参考executewithdelay.md、executeasync.md、delayedactions.md迁移指南upgrade-guide.md底层实现LuaMod.cpp函数注册、LuaMod.cpp异步线程与延时调度C Mod 视角的异步 Lua 实例说明CppUserModBase.hpp完整 Lua API 目录lua-api.md赞分享游戏开发逆向工程【免费下载链接】RE-UE4SSInjectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games项目地址https://gitcode.com/gh_mirrors/re/RE-UE4SS点击查看免费下载相关推荐UE4SS ExecuteAsync 详解异步回调执行机制、弃用原因与 Delayed Action System 迁移指南UE4SS ExecuteAsync 详解异步回调执行机制、弃用原因与 Delayed Action System 迁移指南 ExecuteAsync 是 R游戏开发逆向工程three.js 中的 Clock 时间类getDelta 与 getElapsedTime 的实现原理、用法及 Timer 迁移指南three.js 中的 Clock 时间类getDelta 与 getElapsedTime 的实现原理、用法及 Timer 迁移指南 本文以 three.j前端3D渲染图形学sinon 中 stub.callsArgWithAsync 深度解析异步回调参数注入的实现原理与实战sinon 中 stub.callsArgWithAsync 深度解析异步回调参数注入的实现原理与实战 stub.callsArgWithAsync inde测试开发工具上一篇superpowers-zh 在 Hermes Agent 中的完整安装与使用指南全局加载、config.yaml 登记与工具映射下一篇游戏音频解密终极指南5分钟快速解锁加密游戏音效创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考