한밤에서는 전투 정보를 기반으로 한 애드온의 능력을 제한하기 위해 새로운 시스템과 API를 도입되었습니다. 블리자드는 많은 API를 전면적으로 제한하거나 프레임을 보안 환경(secure environment)으로 이동시키는 대신, 비밀 값(Secret Values)이라 부르는 새로운 기술을 도입했습니다.
보안 환경 정책
비밀 값을 설명하기 전에 먼저 월드 오브 워크래프트에서 애드온의 기능을 제한하기 위해 기존에 도입한 정책을 알아봐야 합니다. 보안 환경 정책은 불타는 성전 패치(2.0) 때 도입되었습니다. 보안 환경 정책은 애드온의 기능을 크게 제한하며, 부정 행위로부터 보호하기 위해 도입하였습니다.
보안 환경 정책이 도입되기 이전에는 애드온이 목표 대상과 시전할 주문을 알아서 선택해주니 게임플레이를 대폭 자동화할 수 있었습니다. 패치 2.0 이후에는 애드온이 자동으로 선택할 수 없으며 플레이어가 행동을 결정해야 합니다. 애드온은 여전히 인간 플레이어에게 주문을 제안할 수 있지만, 플레이어를 대신해 어떤 결정도 내릴 수 없습니다.
오염된 코드
월드 오브 워크래프트가 Lua 코드를 처리할 때, 안전한 상태로 시작되며 모든 상황에서 보호된 함수를 처리할 수 있습니다. 오염(taint)을 만나기 전까지는 안전한 상태를 유지합니다.
함수나 객체가 블리자드 정식 UI가 아닌 신뢰할 수 없는 출처(애드온이나 스크립트)에서 제작된 것이라면 안전한 상태가 깨지고, 오염된 코드로 인식합니다. 오염된 코드를 처리하는 즉시 오염된 코드가 반환되며, 오염된 코드에서 나오는 데이터도 같이 오염됩니다. 한 번 안전한 상태가 깨지면 원상복구가 불가능합니다.
블리자드가 지정한 보호된 기능에 오염된 코드를 사용하려고 하면 보호된 기능이 작동하지 않게 됩니다. 오염된 코드는 플레이어가 재접속하거나 인터페이스를 다시 불러올 때까지 게임 내내 유지됩니다.
오염된 코드를 보호된 함수로 전파하는 것을 방지하고자 hooksecurefunc 또는 HookScript 같은 API를 사용할 수 있습니다. 또한 InCombatLockdown() 같은 API를 이용하여 전투 중에 오염된 코드를 사용하는 것을 방지할 수 있었습니다.
보안 환경 정책은 강압적
몇몇 애드온을 제한하기 위해 기존 보안 환경 정책을 활용하려고 하니 지나치게 제한이 많이 걸리는 점을 제작진은 인정하고 있습니다. 따라서 제작진은 새로운 제한 방법을 고안해냅니다.
비밀 값(Secret values)
비밀 값은 오염된 코드가 Lua 값에 대한 특정 작업을 처리하는 능력을 제한하는 새로운 메커니즘입니다.
대부분의 전투 API 함수는 호출 시 비밀 값을 반환할 수 있습니다. 예: UnitHealth(unit)
오염된 코드는 변수(variables)나 테이블 필드(Table field)에 저장하거나 다른 함수에 전달하는 경우를 제외하고는 비밀 값에 대한 작업을 처리할 수 없습니다.
일부 고유 API는 오염된 코드로부터 비밀 값을 받아들입니다. 예를 들어 StatusBar:SetValue(value)가 있습니다.
위젯 API의 경우, 후술할 비밀 속성(Secret Aspect) 시스템과 연결됩니다.
오염되지 않은 안전한 상태에서는 비밀 값이 일반 값과 실질적으로 동일하며, 이에 대한 어떠한 작업도 차단되지 않습니다.
애드온은 다음 함수를 통해 비밀 값을 검사할 수 있습니다.
issecretvalue(value) 비밀 값인 경우 true를 반환합니다.
canaccessecrets() 즉시 호출된 함수가 비밀 값에 접근할 수 없는 경우(오염된 처리일 때) false를 반환합니다.
canaccessvalue(value) 주어진 값이 비밀이 아니거나, 호출 함수가 비밀 값에 접근할 수 있는 권한이 있을 경우 true를 반환합니다.
API 문서 변경
블리자드 API 문서가 갱신되어 API가 비밀 요소와 어떻게 작동하는지 상세히 설명합니다.
비밀 값을 무조건 반환하는 함수는 SecretReturns = true로 표시됩니다.
조건부로 비밀 값을 반환하는 함수도 존재합니다.
UnitName(unit) 전투 중 NPC 유닛이나 펫 유닛을 지정할 경우 비밀 값을 반환합니다 SecretNonPlayerUnitOrMinionWhileInCombat = true
UnitClass(unit)은 첫 번째 반환값을 조건부로 비밀로 표시합니다. ConditionalSecret = true
함수가 비밀 값을 인자로 받아들이는지 여부는 SecretArguments 필드를 통해 문서화됩니다.
AllowedWhenUntainted 오염되지 않은 처리일 때만 비밀 값을 허용합니다.
AllowedWhenTainted 항상 비밀 값을 받아들일 수 있습니다.
NotAllowed 어떠한 경우라도 비밀 값을 절대 받아들이지 않습니다.
제한 사항
오염된 코드에 대한 기술적 제한 사항의 전체 목록은 다음과 같습니다.
허용되지 않는 작업이 수행될 경우, 결과는 즉시 Lua 오류가 발생합니다.
오염된 코드는 비밀 값을 변수, upvalue(상위값) 또는 테이블의 값으로 저장할 수 있습니다.
오염된 코드는 비밀 값을 Lua 함수에 전달할 수 있습니다.
C 함수의 경우, API는 오염된 호출자로부터 비밀을 수락하도록 명시적으로 표시되어야 합니다.
오염된 코드는 비밀 값을 결합(concatenate)할 수 없습니다.
오염된 코드는 비밀 값에 대한 산술 연산을 수행할 수 없습니다.
오염된 코드는 비밀 값에 대한 비교 또는 논리 연산을 수행할 수 없습니다.
오염된 코드는 비밀 값에 길이 연산자(#)를 사용할 수 없습니다.
오염된 코드는 비밀 값을 테이블의 키로 저장할 수 없습니다.
오염된 코드는 비밀 값에 대한 인덱스 접근 또는 할당을 수행할 수 없습니다. (예: secret[“foo”] = 1)
오염된 코드는 비밀 값을 함수인 것처럼 호출할 수 없습니다.
현재 type(secret)은 비밀 값의 실제 유형을 반환하지만, 이는 변경될 수 있으며 곧 secret을 반환할 수 있습니다.
비밀 테이블 (Secret tables)
Lua 테이블에는 몇 가지 추가 제한 사항이 적용됩니다.
오염되지 않은 코드는 비밀 값을 테이블 키로 저장할 수 있으나, 해당 테이블은 객체 수준에서 영구적으로 비밀 테이블로 바뀝니다.
비밀 테이블은 오염되지 않은 코드가 접근하면 항상 비밀 값을 반환합니다.
오염된 코드는 비밀 테이블에 접근할 수 없습니다. 비밀 테이블에 대한 인덱싱, 할당, 측정, 반복문 시도는 오류가 발생합니다.
비밀 테이블을 처리하기 위한 몇 가지 추가 API가 추가됩니다.
issecrettable(table) 비밀 테이블인 경우 true를 반환합니다.
canaccesstable(table) 비밀 테이블이 아니거나 호출 함수에 대한 비밀 접근이 허용된 경우 true를 반환합니다.
비밀 속성 (Secret aspects)
비밀 값을 받아들이는 위젯 API는 함수 호출의 일부로 객체에 비밀 속성을 적용할 수 있습니다. 속성이 적용되면 동일한 위젯의 다른 API도 비밀 값을 반환하기 시작할 수 있습니다.
예를 들어, UnitName에서 얻은 비밀 문자열 값으로 FontString:SetText(text)를 호출하면 Text 속성이 비밀 속성으로 전환됩니다. 이 속성은 이후 FontString:GetText() 호출 시 모두 비밀 값을 반환하게 합니다.
현재 비밀 속성은 ScriptRegion:SetToDefaults() 호출로만 해제할 수 있습니다.
API 호출 시 여러 비밀 속성을 적용하거나, 반환값의 비밀성이 여러 속성의 영향을 받을 수 있습니다.
API 문서를 참고하면 인자와 반환값 모두에 연결된 API의 전체 속성 집합이 나열되어 있습니다.
비밀 속성 적용 여부는 ScriptRegion:HasSecretAspect(aspect)로 시험할 수 있습니다.
비밀 앵커(Secret anchors)
비밀 값을 허용하지만 연결된 속성이 없는 API는 전체 객체를 비밀 값으로 간주합니다.
예를 들어, 비밀 숫자 값으로 StatusBar:SetValue(value)를 호출하면 명시적인 속성이 적용되지 않습니다. 대신 객체 자체가 비밀 값을 가진 것으로 간주합니다.
비밀 값을 가진 것으로 표시된 객체는 반환값이 속성에 기반하지 않는 모든 API가 비밀 값을 반환하도록 합니다.
이 상태 적용 여부는 ScriptRegion:HasSecretValues()를 통해 테스트할 수 있습니다.
비밀 값을 가지는 것으로 표시된 속성 없는 객체는 anchor 및 위치 API에 영향을 미칩니다.
비밀 값을 가지는 것으로 표시된 객체는 프레임 상의 모든 anchor 및 위치 데이터도 비밀로 표시합니다.
앵커 및 위치 데이터의 비밀성은 하위 종속 anchor로 전파됩니다.
자식 프레임 B가 부모 프레임 A에 anchor되어 있고 A가 비밀 anchor 데이터를 가질 경우, B도 암묵적으로 비밀 anchor 데이터를 가지게 됩니다.
비밀을 anchor에 API를 호출하면 비밀 값을 반환하지 않고 오류가 발생합니다.
이 상태를 테스트하려면 ScriptRegionResizing:IsAnchoringSecret() API를 사용할 수 있습니다.
anchor를 초기화하면 상태를 재설정할 수 있습니다.
상수 접근자(Constant accessors)
일부 API는 상수 비밀 접근자(constant secret accessors)로 표시됩니다. 비밀 값으로 이러한 함수를 호출해도 어떤 속성도 적용되지 않으며 객체가 비밀 값을 가지는 것으로 표시되지도 않습니다. 다만 함수의 반환 값은 암묵적으로 비밀로 처리됩니다.
이러한 함수의 예로는 ScriptRegion:GetHeight(ignoreRect)가 있습니다.
이 함수는 프레임에 아무것도 할당하지 않으므로, 비밀 부울 값으로 호출되더라도 어떠한 속성도 적용하지 않으며 객체를 비밀 값을 가진 것으로 표시하지 않습니다.
그러나 이 함수의 매개변수 중 하나라도 비밀 값이라면, 이 함수의 반환값은 비밀 값이 됩니다.