mcpField
W
wow-api-lua-environment

wow-api-lua-environment

github.com/jburlison开发4659 次安装37 周前更新
代码仓库

关于 wow-api-lua-environment

面向 World of Warcraft 正式服插件开发者的 Lua 参考技能,涵盖沙箱、污染机制与安全执行,并整理了截至 Patch 12.0.0 的 API 和示例。

主要功能

  1. 01Lua 5.1 沙箱函数与限制
  2. 02污染检查与安全执行
  3. 03战斗锁定与受保护框体
  4. 04计时器、钩子与状态驱动器
  5. 05日志与调试工具

适用场景

  1. 01确认 Lua 函数是否可用
  2. 02诊断插件污染与受阻操作
  3. 03实现计时器或安全函数钩子
  4. 04处理战斗和副本限制

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jburlison/wowaddonapiagents --skill wow-api-lua-environment

安装后在 Claude Code 中输入/wow-api-lua-environment确认已加载。

详细介绍

Lua 环境与安全性(零售版 — 补丁 12.0.0)

WoW Lua 沙箱、安全模型、污染系统、安全执行、计时器、钩子、日志记录和受限操作的全面参考。

来源: https://warcraft.wiki.gg/wiki/World_of_Warcraft_API 安全执行: https://warcraft.wiki.gg/wiki/Secure_Execution_and_Tainting Lua 函数: https://warcraft.wiki.gg/wiki/Lua_functions 截至: 补丁 12.0.0(Build 65655)— 2026 年 1 月 28 日 范围: 仅限零售版。

范围

本技能涵盖:

  • Lua 沙箱 — WoW 的 Lua 5.1 环境、受限标准库、被屏蔽的函数
  • 污染系统 — 插件代码如何被污染,以及受污染的代码无法执行哪些操作
  • 安全执行 — 受保护函数、安全框架、安全处理程序
  • 战斗锁定 — 战斗期间插件可以和不可以执行的操作
  • C_Timer — 计时器函数(After、NewTicker、NewTimer)
  • 钩子 — hooksecurefunc、securecallfunction、securecallmethod
  • C_RestrictedActions — 插件限制状态查询
  • C_Log — 日志工具
  • FrameScript — 框架脚本环境、秘密值、清理
  • 调试 — 错误处理、堆栈跟踪、调试工具

何时使用此技能

在以下情况下使用此技能:

  • 了解 WoW 中有哪些 Lua 函数可用、哪些被屏蔽
  • 处理或调试污点问题
  • 编写与安全/受保护框架交互的代码
  • 使用计时器、延迟执行或定时循环模式
  • 安全地挂钩现有函数
  • 了解战斗锁定限制
  • 处理插件限制状态(12.0.0 实例限制)
  • 记录调试消息
  • 使用秘密值和 FrameScript 沙箱

WoW Lua 5.1 沙箱

WoW 使用 Lua 5.1.4,并进行了大量修改。以下标准库函数被屏蔽或移除:

被屏蔽的标准函数
被屏蔽的函数原因
loadfile()无法访问文件系统
dofile()无法访问文件系统
io.*无法访问文件系统
os.execute()无法访问 shell
os.exit()无法关闭客户端
os.remove()无法访问文件系统
os.rename()无法访问文件系统
os.tmpname()无法访问文件系统
os.getenv()无法访问环境变量
package.*无软件包系统
require()无法加载模块
module()无模块系统
newproxy()已移除
getfenv()受限 — 返回只读环境
setfenv()受到严格限制
collectgarbage()可用模式受限
可用的标准函数

大多数 Lua 核心函数均可正常使用:

  • 所有 string.*、table.*、math.* 函数
  • type()、tostring()、tonumber()、rawget()、rawset()、rawequal()、rawlen()
  • pairs()、ipairs()、next()、select()、unpack()
  • pcall()、xpcall()、error()、assert()
  • setmetatable()、getmetatable()
  • coroutine.*(完整支持协程)
  • os.time()、os.date()、os.clock()、os.difftime()
  • print() — 输出到默认聊天框
WoW 新增的全局函数
函数描述
strsplit(delimiter, str [, pieces])按分隔符拆分字符串
strsplittable(delimiter, str [, pieces])拆分为表
strjoin(delimiter, ...)拼接字符串
strtrim(str [, chars])去除空白字符
tContains(table, value)表中是否包含该值?
tInsert(table, value)插入到表中(别名)
tDeleteItem(table, value)移除该值的首次出现
tInvert(table)反转键值对
wipe(table)清空表(保留引用)
CopyTable(table [, shallow])深拷贝或浅拷贝
MergeTable(dest, source)将 source 合并到 dest
Mixin(object, ...)将 mixin 方法复制到对象
CreateFromMixins(...)通过 mixin 创建新对象
CreateAndInitFromMixin(mixin, ...)创建并调用 Init
format(formatString, ...)string.format 的别名
tostringall(...)将所有参数转换为字符串
DevTools_Dump(value, startKey)转储值以进行调试

污染系统

所有插件代码都以**“已污染”(不安全)状态运行。暴雪 UI 代码以“安全”**(未污染)状态运行。污染系统会阻止插件调用受保护函数或修改安全框架。

污染如何运作
  1. 插件代码设置的任何变量都会变为已污染
  2. 污染值会传播——如果污染数据流入暴雪代码,该路径就会受到污染
  3. 受保护函数会在执行前检查污染状态——如果执行路径受到污染,就会失败
  4. 安全框架会继承其创建上下文的安全状态
检查污染状态
-- Check if a global variable is tainted
local isTainted, source = issecurevariable("SomeGlobalVar")
-- isTainted: false = secure, true = tainted
-- source: string name of the addon that tainted it (or nil if secure)

-- Check table field
local isTainted, source = issecurevariable(someTable, "someKey")
常见的污染陷阱
-- WRONG — This taints the Blizzard settings table
Settings.RegisterAddOnCategory = myFunc  -- TAINT!

-- WRONG — Modifying secure frame in insecure context
local btn = PlayerFrame  -- This is a secure Blizzard frame
btn:SetAttribute("type", "spell")  -- TAINT — can cause action blocked errors

-- RIGHT — Use hooksecurefunc for observation without tainting
hooksecurefunc("SomeBlizzardFunction", function(...)
    -- Your code runs AFTER the original — doesn't taint
end)

安全执行与受保护函数

受保护函数的限制

标记为 #protected 的函数只能由以下代码调用:

  • 安全(暴雪)代码
  • 由硬件事件触发的安全点击处理程序
  • SecureActionButtonTemplate 处理程序内部

受保护函数包括:

  • 所有与战斗相关的施法操作:CastSpellByName()、CastSpellByID()、UseAction()
  • 使用物品:UseItemByName()、UseContainerItem()(战斗中)
  • 更改目标:TargetUnit()、AssistUnit()、FocusUnit()
  • 移动:MoveForwardStart()、JumpOrAscendStart()
  • UI 状态:对安全框架调用 SetAttribute()(战斗中)
战斗锁定
-- Check if in combat lockdown
if InCombatLockdown() then
    -- Cannot: create/destroy secure frames, change secure attributes
    -- Cannot: set points on secure frames, change parent/visibility of secure frames
    -- Can: read attributes, modify non-secure frames, queue changes for later
    return
end

-- Queue changes for after combat
local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_REGEN_ENABLED")
frame:SetScript("OnEvent", function()
    -- Combat ended — safe to modify secure frames now
    DoSecureFrameChanges()
end)
安全处理程序与模板
-- SecureActionButtonTemplate — allows protected actions via user clicks
local btn = CreateFrame("Button", "MySecureBtn", UIParent, "SecureActionButtonTemplate")
btn:SetAttribute("type", "spell")
btn:SetAttribute("spell", "Fireball")
-- When clicked by hardware event, this will cast Fireball

-- SecureHandlerBaseTemplate — run secure snippets
local frame = CreateFrame("Frame", nil, UIParent, "SecureHandlerBaseTemplate")
frame:SetAttribute("_onstate-combat", [[
    -- This snippet runs in the secure environment
    if newstate == "combat" then
        self:Hide()
    else
        self:Show()
    end
]])
RegisterStateDriver(frame, "combat", "[combat] combat; nocombat")
状态驱动器
-- Register a state driver for automatic secure attribute updates
RegisterStateDriver(frame, "stateName", "conditionalString")
-- e.g., RegisterStateDriver(frame, "visibility", "[combat] hide; show")

UnregisterStateDriver(frame, "stateName")

C_Timer — 计时器 API

Wiki: https://warcraft.wiki.gg/wiki/API_C_Timer.After

计时器函数
函数返回值描述
C_Timer.After(seconds, callback)—单次计时器
C_Timer.NewTimer(seconds, callback)timer可取消的单次计时器
C_Timer.NewTicker(seconds, callback [, iterations])ticker重复计时器
计时器对象方法
local timer = C_Timer.NewTimer(5, function()
    print("5 seconds elapsed")
end)
timer:Cancel()  -- Cancel before it fires

local ticker = C_Timer.NewTicker(1, function()
    print("Every second")
end, 10)  -- Stop after 10 iterations
ticker:Cancel()  -- Or cancel early

-- Simple delay (non-cancellable)
C_Timer.After(2, function()
    print("2 seconds later")
end)

Hooks — 函数挂钩

hooksecurefunc

主要的安全挂钩机制。你的挂钩会在原函数之后运行,且不会污染原函数。

-- Hook a global function
hooksecurefunc("UseAction", function(slot, checkCursor, onSelf)
    print("Action used:", slot)
end)

-- Hook a method on an object
hooksecurefunc(GameTooltip, "SetUnitAura", function(self, ...)
    -- Runs after GameTooltip:SetUnitAura
end)

-- IMPORTANT: You CANNOT prevent the original from executing
-- IMPORTANT: You CANNOT modify the return values
-- IMPORTANT: Your hook does NOT taint the original function
securecallfunction / securecallmethod
-- Call a function in secure context (if possible)
securecallfunction(func, arg1, arg2)

-- Call a method in secure context
securecallmethod(object, "MethodName", arg1, arg2)

C_RestrictedActions — 插件限制状态

12.0.0 中新增。用于追踪插件限制何时生效(例如,在副本内)。

函数返回值描述
C_RestrictedActions.GetAddOnRestrictionState(type)state当前限制状态
C_RestrictedActions.IsAddOnRestrictionActive(type)active限制当前是否生效?
C_RestrictedActions.CheckAllowProtectedFunctions(object [, silent])protectedFunctionsAllowed对象能否调用受保护函数?
InCombatLockdown()inCombatLockdown战斗锁定是否生效?
限制事件
事件描述
ADDON_RESTRICTION_STATE_CHANGED限制状态已更改(进入或离开副本)
PLAYER_REGEN_DISABLED进入战斗
PLAYER_REGEN_ENABLED离开战斗

C_Log — 日志记录

FunctionDescription
C_Log.LogMessage(message)记录信息消息
C_Log.LogWarningMessage(message)记录警告
C_Log.LogErrorMessage(message)记录错误
C_Log.LogMessageWithPriority(priority, message)使用指定优先级记录

注意:ConsolePrint() 已在 12.0.0 中移除。请改用 C_Log.LogMessage()。


FrameScript 函数

WoW 提供了用于处理安全值/秘密值系统的特殊 FrameScript 函数:

FunctionReturnsDescription
issecurevariable([table,] name)isSecure, taintSource检查污染状态
issecretvalue(value)isSecret值是否为秘密值?
issecrettable(table)isSecretOrContentsSecret表或其内容是否为秘密值?
canaccessvalue(value)isAccessible插件能否访问此值?
hasanysecretvalues(values)isAnyValueSecret是否有任意参数为秘密值?
scrubsecretvalues(values)scrubbed将秘密值替换为 nil
secretwrap(values)wrapped将值包装为秘密值
mapvalues(func, values)mapped对值应用函数(可安全处理秘密值)
securecallfunction(func, ...)results在安全上下文中调用
securecallmethod(obj, method, ...)results在安全上下文中调用方法
forceinsecure()—强制以非安全方式执行
seterrorhandler(handler)—设置全局错误处理程序
geterrorhandler()handler获取当前错误处理程序

调试工具

错误处理
-- Set a custom error handler
seterrorhandler(function(msg)
    -- msg is the error string
    print("ERROR:", msg)
end)

-- Protected call with error handling
local success, err = pcall(function()
    -- Code that might error
end)
if not success then
    print("Error:", err)
end

-- xpcall with message handler
local success, err = xpcall(function()
    error("something broke")
end, function(msg)
    return msg .. "\n" .. debugstack(2)
end)
调试堆栈与信息
-- Get a stack trace
local stack = debugstack([thread,] [start [, count1 [, count2]]])

-- Get debug info
local info = debuglocals([thread,] [level])

-- Profile timing
debugprofilestart()
-- ... code to measure ...
local elapsed = debugprofilestop()  -- microseconds
调试用斜杠命令
-- /dump expression — evaluates and prints
-- /run code — executes Lua code
-- /script code — same as /run
-- /console cvarName [value] — get/set console variables

常见模式

延迟初始化(等待登录)
local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_LOGIN")
frame:SetScript("OnEvent", function(self, event)
    -- Safe to initialize — player is logged in
    self:UnregisterEvent(event)
    InitializeAddon()
end)
安全的 OnUpdate 节流
local elapsed = 0
local THROTTLE = 0.1  -- 100ms
frame:SetScript("OnUpdate", function(self, dt)
    elapsed = elapsed + dt
    if elapsed < THROTTLE then return end
    elapsed = 0
    -- Do periodic work
end)
战斗结束后的操作队列
local pendingActions = {}

local function QueueAction(action)
    if InCombatLockdown() then
        tinsert(pendingActions, action)
    else
        action()
    end
end

local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_REGEN_ENABLED")
frame:SetScript("OnEvent", function()
    for _, action in ipairs(pendingActions) do
        action()
    end
    wipe(pendingActions)
end)
优雅处理秘密值(12.0.0)
-- When values might be secret, pass them directly to widgets
local name = UnitName(unit)  -- may be secret
myFontString:SetText(name)   -- widgets accept secrets

-- Check if a value is secret before trying operations
if not issecretvalue(someValue) then
    -- Safe to compare, do arithmetic, etc.
    if someValue == "expected" then ... end
else
    -- Cannot inspect — pass to UI widget directly
    myWidget:SetText(someValue)
end

注意事项与限制

  1. 不支持 require() — WoW 没有模块系统。使用 TOC 文件控制加载顺序。库会直接嵌入。
  2. setfenv() / getfenv() — 受到严格限制。不要依赖环境操作。
  3. collectgarbage() — 只有 "count" 模式可用。无法强制执行 GC。
  4. 污染具有粘性 — 变量一旦被污染,就会一直保持污染状态。即使将其设回原始值,污染仍然存在。
  5. print() 会输出到聊天框 — 与标准 Lua 不同,print() 会输出到默认聊天框,而不是 stdout。
  6. 字符串库扩展 — WoW 将 strsplit、strjoin、strtrim 和 strmatch 添加为全局函数(此外还有 string.match)。
  7. 不支持 os.exit() — 无法通过程序终止客户端。
  8. 支持协程 — 提供完整的协程支持,且常用于异步模式。
  9. 秘密值(12.0.0) — 现在,某些 API 的返回值是不透明的“秘密”值,无法检查、比较或用于算术运算。完整详情请参阅 wow-api-important 指南。
  10. 实例限制(12.0.0) — 在实例中会阻止 SendAddonMessage()。设计插件时,应确保在副本内容中无需玩家间通信也能正常工作。

评论

支持 Markdown