![]()
![]()
來自哀冬大神4月新作,小編注:我知道大家看不懂,想要直接的拿來就按的一鍵傻瓜式,正在整理
喜歡研究的可以看哀冬這次的源碼:https://cn.wago.io/0jyJ6PFcF
不喜歡研究等喂飯的一兩天整理好發(fā)布引言插件系統(tǒng)是《魔獸世界》開放性的基石,游戲的設計師們在玩家創(chuàng)造力與游戲公平性之間,也構(gòu)建了一道看似不可逾越的屏障。而虛空之花正扎根在這道屏障上的裂隙優(yōu)雅綻放。本文將基于虛空之花的歷代內(nèi)核原理介紹逐步向大家揭示魔獸世界插件系統(tǒng)中的高級框體設計技巧,也作為第五代內(nèi)核源碼開放的序幕。第一章 移形換影-框體和安全框架隔離系統(tǒng)簡介
魔獸世界的插件系統(tǒng)建立在嚴格的安全框架之上。而整個UI設計中的框體分為兩類:普通框體(Frame)和安全框體(SecureFrame)。普通框體可以自由修改大小、位置、紋理等屬性,但無法綁定任何戰(zhàn)斗動作;安全框體(如SecureActionButtonTemplate)則能綁定技能、物品、宏命令,卻受到“限制環(huán)境”(Restricted Environment)的嚴格約束——戰(zhàn)斗中,任何來自“非安全代碼”(通常指來自玩家的插件代碼)的修改企圖都會被系統(tǒng)拒絕。該安全框架系統(tǒng)自TBC開始上線一直持續(xù)至今,用以保護游戲的開放API不被濫用于破壞公平性的插件。
第一代虛空之花的方案十分的簡單,因為當時安全框體雖然不允許修改綁定的技能但是居然可以在戰(zhàn)斗中調(diào)用SetPoint修改位置。我們只需在戰(zhàn)斗前,通過CreateFrame創(chuàng)建數(shù)十個綁定好技能的安全按鈕,并將其預先放置于屏幕可視區(qū)域之外。而戰(zhàn)斗中,當APL(輸出優(yōu)先級循環(huán)邏輯的縮寫)計算出當前最佳釋放技能時,直接將對應的按鈕通過SetPoint函數(shù)瞬移到屏幕中的固定位置。玩家只需持續(xù)點擊該位置,就會自動點中當前最優(yōu)技能按鈕。始于簡單粗暴終于簡單粗暴,在WLK懷舊服更新幻化的版本后,安全框體無法再在戰(zhàn)斗中被非安全代碼移動了。
第二章 光暗交織-安全框體回調(diào)系統(tǒng)簡介
安全框體并不是完全無法在戰(zhàn)斗中修改的,它只是不能被“非安全”代碼修改,那么什么是安全代碼呢?安全代碼包括了游戲UI自帶的代碼以及,重點來了——還包括玩家在戰(zhàn)斗之外通過開放API接口預先綁定在特定安全事件上的預設代碼片段!看似復雜,但是我們參考一下安全回調(diào)注冊API的解釋(https://warcraft.wiki.gg/wiki/API_SecureHandlerWrapScript)。這個接口允許為安全框體的事件(如OnClick、OnEnter)注冊一些代碼片段以字符串形式注入安全框架內(nèi)執(zhí)行。唯一的限制是這串代碼必須在戰(zhàn)斗前預設,且安全區(qū)內(nèi)與安全區(qū)外的執(zhí)行環(huán)境完全不同,只允許有限的函數(shù)被玩家調(diào)用,具體參考(https://warcraft.wiki.gg/wiki/RestrictedEnvironment)。
同時,雖然在戰(zhàn)斗中無法通過“非安全”代碼修改安全框體屬性,但是我們可以修改普通框體的屬性來干擾玩家對安全框體的操作。比如將一個普通框體覆蓋在安全框體的上層,通過控制普通框體的尺寸大小來控制玩家是否能點擊到安全框體。結(jié)合函數(shù)列表查閱我們又注意到了SetPropagateMouseClicks,這個函數(shù)允許將框體設置成可以點擊穿透。那么一切就緒,第二代方案思路已經(jīng)構(gòu)成。將一大堆框體按照“普通-安全-普通-安全”這樣交叉疊放,其中普通框體是否穿透受APL控制,安全框體始終可以穿透。當一次玩家點擊可以穿透多少層普通框體就代表可以觸發(fā)幾次安全框體的OnClick事件,然后我們記錄這些事件的發(fā)生次數(shù)就可以在安全區(qū)內(nèi)得到非安全區(qū)傳遞的信息。
該方案發(fā)布一個月后,游戲中修改了戰(zhàn)斗中的點擊穿透機制,從此在戰(zhàn)斗中安全框體無法再被穿透了,為此不惜連帶影響了一批UI插件。但這次經(jīng)歷讓我們深刻理解了安全框體的事件回調(diào)機制,并證明安全框架并非絕對安全——只要存在交互,就存在信息傳遞的可能性!
第三章 量子糾纏-框體聯(lián)動關系簡介
前兩代方案都依賴鼠標交互,戰(zhàn)斗中不斷點擊鼠標在走位頻繁的戰(zhàn)斗中非常不便。這讓我們思考是否存在非交互型的技術路線,而直接從安全框架設計的邏輯上找到突破口。我們仔細研究框體的每一個API(https://warcraft.wiki.gg/wiki/Widget_API)。
注意到CreateFrame接口的第三個參數(shù)叫做parent,這是給一個框體創(chuàng)建時指定它的父親。這個設計的存在可以讓新創(chuàng)建的框體繼承父框體的屬性并讓他們建立依附關系。比如一個大界面下可以依附許多按鈕,當你關閉界面時界面內(nèi)依附的按鈕也會一起消失,這種同進同退的聯(lián)動關系極大的方便了界面開發(fā),不再需要細枝末節(jié)的控制每一個框體的細節(jié)動作。你可以在游戲中輸入/fstack來觀察游戲界面內(nèi)的每一個框體和他們的依附關系。那么能否將非安全框體和安全框體聯(lián)動起來?SetParent接口進入了視野,這個接口允許我們在戰(zhàn)斗中動態(tài)的修改框體父子關系。但它也存在巨大的限制,即當安全框體設定普通框體作為父節(jié)點時,系統(tǒng)為了防止非安全代碼通過父節(jié)點影響安全子節(jié)點,會將整個父節(jié)點升格為安全框體。這種“安全提升”機制本意是為了防止玩家濫用,但實際上卻成了一根筋兩頭堵。
第三代方案基于對此機制的利用,我們在戰(zhàn)斗前創(chuàng)建普通父框體,設置其各種屬性(比如以大小尺寸數(shù)值作為信息傳遞通道),然后讓其領養(yǎng)一個安全按鈕作為子節(jié)點。在父子綁定的瞬間,父框體被提升為安全框體,其當前所有屬性被連帶進入到安全環(huán)境中。戰(zhàn)斗中我們就可以直接讀取這個升格為安全框體的普通父框體屬性作為信息傳遞通道在安全區(qū)內(nèi)直接使用!
第三朵虛空之花在ICC開放前夜發(fā)布,由于其完全不依賴鼠標純鍵盤驅(qū)動的良好體驗,引發(fā)了社區(qū)劇烈震蕩,乃至于全網(wǎng)貓德外掛都頂著哀冬的名號蹭熱度。直到一個多月后,開放API的版本更新,戰(zhàn)斗中任何修改安全框體父子關系的操作都會失敗,但邏輯路線的突破口已經(jīng)證明了所謂的“安全框架”表現(xiàn)并不理想。
第四章 無中生有-污染系統(tǒng)簡介
第四套方案的故事開始之前,我們要先提到一個重要的概念“污染”。前面提到的“安全”和“不安全”,是面向框體這類數(shù)據(jù)結(jié)構(gòu)的屬性概念,它標記了一個框體能辦到什么不能辦到什么。而污染則是面向執(zhí)行狀態(tài)的概念,標記著當前執(zhí)行流程能辦到什么不能辦到什么。插件系統(tǒng)總是從游戲原生代碼開始執(zhí)行,一直保持干凈狀態(tài)直到調(diào)用到玩家的第三方插件代碼就會變成污染狀態(tài)。污染狀態(tài)下寫入的數(shù)據(jù)也會被污染,而原本干凈狀態(tài)執(zhí)行的代碼讀到這些被污染的數(shù)據(jù)又會被傳染成污染狀態(tài),被污染的代碼和數(shù)據(jù)將無法在戰(zhàn)斗中正常工作。這套機制嚴格的保證了原生代碼不會被第三方代碼劫持從而實現(xiàn)一些不合規(guī)的操作。
過去的多個版本我們一直期待著有一個“信使”能夠從非安全區(qū)出發(fā)把信息傳遞到安全區(qū),或者反過來從安全區(qū)出發(fā)把非安全區(qū)信息帶回,但是安全框架隔離機制阻止了這個過程,讓“信使”總是無功而返。那么仔細想一想一個問題——沒有信息本身是不是一種信息?結(jié)合前面提到的“污染”再思考一下呢?4.x內(nèi)核的方案原理就基于【這段文字好像覆蓋滿了觸手,所以我們決定暫不揭曉】,從而實現(xiàn)了信息的傳遞。
第四朵虛空之花的根深深的扎在了整個安全框架之上,猶如上古之神的根須已與艾澤拉斯的中樞糾纏在一起難解難分,為什么當年阿曼蘇爾拔掉亞煞極之后不繼續(xù)拔掉克蘇恩、尤格薩隆、恩佐斯呢?
第五章 時空轉(zhuǎn)換-原生UI之殤
之前第二代方案是基于玩家鼠標與框體空間上的互動來實現(xiàn)的,即一次操作多重觸發(fā)。在版本更新后,一次用戶操作(比如點擊、移動)只能讓一個安全框體響應一次了。那有沒有辦法讓信息不經(jīng)過用戶操作而觸發(fā)變化的呢?我們視野回到安全區(qū)內(nèi)的函數(shù)列表(https://warcraft.wiki.gg/wiki/RestrictedEnvironment)的第一個函數(shù)SecureCmdOptionParse,它的作用是用來解析宏命條件的。宏的解析不需要玩家的實際操作,當你的鼠標指向敵人時候SecureCmdOptionParse("[@mouseover,harm]0;1")的結(jié)果就是0,反之就是1。所以我們以時間換空間,創(chuàng)造一個類型為“SecureUnitButtonTemplate”的敵對目標框體,再在它的之上覆蓋一層不可指向的普通框體,從而控制玩家鼠標是否能指向這個敵對目標框體來影響宏條件判定進行一連串1和0的信息的傳遞。方案成功了?
不,還沒有成功,上述方案還欠缺重要的一個環(huán)節(jié),安全區(qū)內(nèi)沒有觸發(fā)源來驅(qū)動SecureCmdOptionParse的宏判定。如果讓人手工來狂按鼠標鍵盤進行觸發(fā)的話體驗太糟糕了(之前的B站up主@隱幻殺曾經(jīng)嘗試到了這一步)。我們需要找到一個在安全區(qū)可以穩(wěn)定的觸發(fā)源,那么一定有聰明人想到了SecureHandlerWrapScript不是可以注冊OnHide和OnShow事件嗎?我們讓框體在觸發(fā)OnShow回調(diào)的時候調(diào)用Hide函數(shù),然后再在觸發(fā)OnHide回調(diào)的時候調(diào)用Show函數(shù)。豈不是可以讓一個安全框體一直閃爍,在閃爍的安全回調(diào)代碼中判定SecureCmdOptionParse。方案成功了?
不,還沒有成功,因為聰明的游戲設計師也考慮到了這一點。SecureCmdOptionParse的判定依賴于界面的真實繪制,憑空在lua代碼里控制消失和顯示并不會真的讓框體馬上消失和顯示,會影響SecureCmdOptionParse的判定。所以第五朵虛空之花綻放的真正核心要素是要在安全區(qū)邏輯內(nèi)找到一個穩(wěn)定的異步觸發(fā)源。于是我們把目光轉(zhuǎn)向了原生UI代碼,從中尋找機會。
我們發(fā)現(xiàn)原生UI接口中,為了解決安全框體自動隱藏接口,后臺設定了一個叫做SecureHoverDriverManager的系統(tǒng)框體對注冊自動隱藏框體的狀態(tài)管理,當沒有需要關注的狀態(tài)時他會隱藏,于是我們利用一個誘餌框體在安全區(qū)內(nèi)調(diào)用RegisterAutoHide引誘其現(xiàn)身后,利用它無事自動隱藏的特性綁定了SecureHoverDriverManager的OnHide回調(diào)作為我們的異步觸發(fā)源。在經(jīng)歷了一段時間的原生UI渲染管線時序摸索后,我們找到了穩(wěn)定每3幀渲染tick觸發(fā)一次1/0bit位傳輸?shù)恼诺馈?/p>
至此第五朵虛空之花湊齊了宏命令判定、框體遮蓋技巧、安全區(qū)異步觸發(fā)源這套OTK卡組,實現(xiàn)了跨越安全框架的信息傳遞!
第六章 凋謝與新生-插件系統(tǒng)的設計哲學之變
回顧這幾朵虛空之花的綻放之姿,我們看到的不僅僅是一連串代碼的進化,更是一群業(yè)余插件開發(fā)者們在游戲劃定的紅線邊緣試探的極致體現(xiàn)。結(jié)合近期正式服大量禁用戰(zhàn)斗類插件API的動作,這類WA的存在引發(fā)了一個深層次的社區(qū)爭論:虛空之花這到底算不算外掛?
對于普通玩家而言,此類WA實現(xiàn)了近乎完美的自動化輸出,功能極其強大,在表現(xiàn)形式上與傳統(tǒng)的按鍵精靈或內(nèi)存掛十分接近,因此很多人將其視為破壞平衡的“外掛”。但從技術合規(guī)性的角度來看,該系列WA從頭到尾沒有讀取任何非法的內(nèi)存數(shù)據(jù),沒有注入任何外部程序,其每一行代碼、每一個機制,都是利用魔獸世界原生提供的合法API和UI機制編寫的。基于這種現(xiàn)實,虛空之花不是掛!
但插件真正的邊界究竟應該劃在哪里?這或許是游戲自身需要去解答的問題。我們很高興看到在正式服中,插件系統(tǒng)的設計哲學已經(jīng)做出了改變,游戲內(nèi)推出的一鍵輸出解決了新手上手困難問題,12.0插件開發(fā)API大幅度刪減也幾乎終結(jié)了BOSS難度和插件軍備競賽的螺旋。而懷舊服作為一段凝固的時光,其插件框架被鎖定在過去。過時的設計和玩家的新需求錯配之下,我們看到的是游戲生態(tài)的持續(xù)惡化,如果繼續(xù)置之不理放任崩壞,那虛空之花也將永不凋謝~
點下方留言 分享你的觀點
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺“網(wǎng)易號”用戶上傳并發(fā)布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.