wow-api-lua-environment について
World of Warcraft Retailのアドオン開発者向けLuaリファレンススキルです。サンドボックス、汚染、安全な実行を扱い、Patch 12.0.0時点のAPIと例をまとめています。
主な機能
- 01Lua 5.1サンドボックスの関数と制限
- 02汚染チェックとセキュア実行
- 03戦闘ロックダウンと保護フレーム
- 04タイマー、フック、ステートドライバー
- 05ログとデバッグ機能
活用シーン
- 01Lua関数の利用可否を確認
- 02アドオンの汚染や実行拒否を調査
- 03タイマーや安全な関数フックを実装
- 04戦闘中やインスタンス内の制限に対応
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jburlison/ wowaddonapiagents --skill wow-api-lua-environmentインストール後、Claude Code で/wow-api-lua-environmentを入力して読み込まれたか確認します。
詳細
Lua 環境とセキュリティ(Retail — Patch 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 最終更新: Patch 12.0.0 (Build 65655) — January 28, 2026 対象範囲: Retail のみです。
対象範囲
このスキルでは、以下を扱います。
- 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() | シェルにアクセスできません |
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, ...) | ミックスインのメソッドをオブジェクトにコピーします |
CreateFromMixins(...) | ミックスインから新しいオブジェクトを作成します |
CreateAndInitFromMixin(mixin, ...) | 作成して Init を呼び出します |
format(formatString, ...) | string.format のエイリアスです |
tostringall(...) | すべての引数を文字列に変換します |
DevTools_Dump(value, startKey) | デバッグ用に値をダンプします |
Taint システム
すべてのアドオンコードは**「tainted」(非セキュア)として実行されます。Blizzard の UI コードは「secure」**(非 tainted)として実行されます。Taint システムは、アドオンが保護された関数を呼び出したり、セキュアフレームを変更したりするのを防ぎます。
Taint の仕組み
- アドオンコードによって設定された変数は、すべて tainted になります
- Tainted な値は伝播します。tainted なデータが Blizzard のコードに流れ込むと、その経路も tainted になります
- 保護された関数は実行前に taint を確認し、実行経路が tainted なら失敗します
- セキュアフレームは、作成されたコンテキストからセキュリティを継承します
Taint の確認
-- 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")
よくある Taint の落とし穴
-- 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 とマークされた関数は、次の場所からのみ呼び出せます。
- セキュアな(Blizzard の)コード
- ハードウェアイベントによって発生したセキュアクリックハンドラー
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
タイマー関数
| 関数 | 戻り値 | 説明 |
|---|---|---|
C_Timer.After(seconds, callback) | — | 1 回限りのタイマー |
C_Timer.NewTimer(seconds, callback) | timer | キャンセル可能な 1 回限りのタイマー |
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
主要な安全なフック機構です。フックは元の関数を taint せずに、実行後に呼び出されます。
-- 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 — ロギング
| 関数 | 説明 |
|---|---|
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 関数があります。
| 関数 | 戻り値 | 説明 |
|---|---|---|
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
注意点と制限事項
require()は使用できません — WoW にはモジュールシステムがありません。読み込み順序の制御には TOC ファイルを使用してください。ライブラリは直接埋め込まれます。setfenv()/getfenv()— 厳しく制限されています。環境操作に依存しないでください。collectgarbage()—"count"モードのみ動作します。GC の実行を強制することはできません。- 汚染は残ります — 変数が一度汚染されると、その状態は維持されます。元の値に戻しても、汚染は残ります。
print()はチャットに出力されます — 標準 Lua とは異なり、print()は標準出力ではなく、デフォルトのチャットフレームに出力します。- 文字列ライブラリの追加機能 — WoW では
strsplit、strjoin、strtrim、strmatchがグローバル関数として追加されています(string.matchに加えて)。 os.exit()は使用できません — プログラムからクライアントを終了することはできません。- コルーチンは使用できます — コルーチンが完全にサポートされており、非同期パターンで一般的に使用されています。
- シークレット値(12.0.0) — 一部の API の戻り値は、検査、比較、算術演算に使用できない不透明な「シークレット」値になりました。詳しくは
wow-api-importantの手順を参照してください。 - インスタンスでの制限(12.0.0) — インスタンス内では
SendAddonMessage()がブロックされます。インスタンスコンテンツ中にプレイヤー間通信を行わなくても動作するよう、アドオンを設計してください。