Unity下Bug修復神器InjectFix正式對外開源
先說幾個亮點:
1、直接在Unity工程上修改C#即可更新;老專案無需修改原有程式碼即可使用;
2、更符合蘋果熱更新條款;
3、每個遊戲一份私有補丁格式,安全更有保障。
InjectFix經騰訊內部多個專案應用反饋十分良好,不僅能解決線上bug,還可以有效的提高日常開發效率,下面我們聊下這專案的前世今生。
熱更方案大亂鬥
所有支援ios的熱更方案都有個共同點:更新後程式碼都是解析執行。如果按其更新前是否解析執行,可以分為兩大類:
一類是某些模組甚至整個遊戲,都一直解析執行。這是最傳統的方式,目前市面上所有主流方案(xLua,slua,tolua,ILRuntime,jsb等等)都支援這種方式。這種方式的特點:
1、或多或少都會有些侵入性:ILRuntime解析執行C#編譯後的程式集,在這些方案裡頭侵入性可能最小,但也需要對程式碼重構,把要更新的邏輯拆到單獨程式集。各種非C#的指令碼侵入性最大,一個已經完成的純C#專案要用意味著重寫。
ps:也有一種思路是通過一個C#轉XX指令碼工具來實現C#編碼,解析執行,但如果你是一個已有專案想這麼轉一下,大概率是失敗的,除非你一開始就在用這方式在開發,碰到坑就避開,因為這類方案往往不是完整支援全部語法,支援的語法也不一定能完全一致。
2、基於效能,實現便利性等的考慮,一般遊戲有些地方要以原生的方式跑,這些原生跑的程式碼出了bug這種方式是無能為力的。
3、如果使用的指令碼是動態型別語言,還會帶來程式碼維護困難的問題。
4、優點是可以新增功能,有的遊戲甚至可以做到一次下載,後續不用整包更新。但蘋果條款分析的章節可以看到,這也不一定是好事。
另外一類是以原生方式跑,如果有bug,把邏輯重定向到新的,解析執行的邏輯。這種方式的特點:
1、侵入性低,後期專案也可以使用。
2、正常邏輯是原生方式執行,有問題只是區域性切換到解析執行,所以效能比較好。
3、會導致程式碼段增大,增大正比於注入的類的數量。
4、這種方式往往難以新增功能。
第二種方式是接下來討論的重點,方便起見,我們稱之為“熱修復”,熱修復最早的成熟方案是xLua提供,經過兩年來的使用已經逐漸被接受,tolua#後來也加入了這功能,也有一些網友基於ILRuntime做了熱修復功能。
InjectFix是什麼?
InjectFix就是一個熱修復的實現。那它和其它熱修復方案又有什麼不同呢?
設想這麼個場景,我們有一個一千行程式碼的函式,其中有一行有問題,我們需要修復它。
如果用xLua,需要用lua去重新實現一遍這個函式,工作量大。而基於ILRuntime的熱修復,由於其補丁是另一個程式集,它無法直接訪問原類的私有成員,所以那999行正常程式碼一般也不能直接使用,需要做較多修改。
而InjectFix不需要用lua,也不需要像ILRuntime熱修復那樣另外建一個工程把那一千行邏輯重實現。只需要在Unity原工程直接改掉這行程式碼,然後標註這函式要更新即可。
不僅如此,InjectFix還有其它優勢:
執行時非常小巧,僅100K左右,比各lua方案,ILRuntime都要小很多,而且不依賴第三方庫,純C#實現。
支援每個遊戲生成一份自己私有的補丁格式,私有的指令定義。這樣相比通用的lua原始碼,lua位元組碼,clr程式集都更安全些。
支援Assembly-CSharp.dll之外的dll的修復。
免程式碼生成,更乾淨。
它也有缺點,不支援新增類,也不支援在已有類新增欄位,修bug還是夠用的,但難以通過熱更為遊戲增加新功能。InjectFix就一個純粹的修bug工具而已。
黑科技
由於InjectFix支援重複載入補丁,新載入補丁會自動覆蓋上一個,這特性可以用來實現真機程式碼邏輯實時修改。
蘋果政策合規性
各熱更方案群的問的頻率最高的問題之一:這方案會不會導致我遊戲蘋果稽核不通過。
讓我們看看蘋果的熱更新條款:
可以看到最新條款允許下載程式碼解析執行,但前提是不能通過新增特性和功能來把程式改得(和稽核時相比)面目全非。再看看通常被拒時的理由中的Guideline 2.5.2裡的一句:Your app, extension, or linked framework appears to contain code designed explicitly with the capability to change your app’s behavior or functionality after App Review approval。
有“新增特性和功能”能力的熱更新方案的尷尬之處在於有“改得面目全非”的能力。而InjectFix從它提供的能力(只能修改已有函式)來看,並不具備“新增特性和功能”的能力,這本來是弱點,放在這裡卻成為合規性的保證了。
基本原理
InjectFix專案的研發挺曲折的。InjectFix和xLua是同一個作者,也是本文筆者,當時xLua開源後,不斷有人提希望提供個C#轉lua的工具,而深入研究覺得實現個il虛擬機器工作量還更小,這樣還能避免lua的一些gc問題。
決定要做il虛擬機器後,也曾想過直接使用ILRuntime,評估後覺得不太符合我們的使用場景:ILRuntime並不能實現和原生程式碼的函式級別配合,這是我們能實現原工程直接改Bug的關鍵;ILRuntime執行時部分依賴cecil,除了資源佔用大之外,還容易和unity自帶或者某些外掛的cecil衝突;載入的是標準的程式集在安全性方面也比較堪憂。雖說這些都可以改,但修改的工作量也挺大的,還不如自己寫一個。
InjectFix實現bug修復主要靠這兩部分:虛擬機器負責新邏輯的解析執行;注入程式碼負責把呼叫重定向到虛擬機器;下面我們結合最簡單的例子介紹下這兩部分。
虛擬機器
關鍵部分用幾行偽碼就可以描述清楚:
導讀
1、pc指向的是函式的第一條指令;
2、argumentBase指向的是第一個引數;
3、while+switch一條條指令往下執行,具體指令的操作在case那;
argumentBase指向的是求值棧該函式的棧幀,棧幀是這麼安排的:
先放引數(如果有的話),再放本地變數(如果有的話),接著是臨時區域,當函式返回時彈掉所有東西,如果有返回值就放到棧頂(函式執行前引數0的位置)。
用如下一個靜態方法來演示下虛擬機器怎麼執行:
- public static float Add(float a, float b)
- {
- return a - b;
- }
這函式編譯後是這四條指令
Add函式的執行過程
1、指令1把引數0 Push到棧頂;
2、指令2把引數1 Push到棧頂;
3、指令3把兩個棧頂元素彈出(Pop)並相加,結果Push到棧頂;
4、指令4把棧頂拷貝到引數0的位置,清理棧,退出迴圈,Execute函式執行結束。
程式碼注入
上面的Add函式注入後是這樣的
- public static float Add(float a, float b)
- {
- if (WrappersManagerImpl.IsPatched(92))
- {
- return WrappersManagerImpl.GetPatch(92).__Gen_Wrap_25(a, b);
- }
- return a - b;
- }
比較簡單,發現這函式有patch的話,就重定向到虛擬機器。
而__Gen_Wrap_25是個介面卡函式,賦值把引數壓棧,呼叫虛擬機器的Execute函式,並把結果返回。__Gen_Wrap_25的實現如下:
- public float __Gen_Wrap_25(float P0, float P1)
- {
- Call call = Call.Begin();
- call.PushSingle(P0);
- call.PushSingle(P1);
- this.virtualMachine.Execute(this.methodId, ref call, 2, 0);
- return call.GetSingle(0);
- }
PS:我們的例子僅有三種指令,和這幾條指令無關的程式碼全部簡化了,真正複雜得多,有興趣可以看原始碼瞭解。
關於開源
閉門造車很難做出好專案。需要用心聆聽,根據反饋不斷的改進自己。而開源,能夠聽到更多的聲音,也能更好的改進這個專案。
總結下
InjectFix使用簡單,小巧,合規且安全。即使你不打算用它來更新線上版本,只要你程式有原生部分,接入也能一定程度上提高開發效率,沒什麼拒絕它的理由,是吧?
相關文章
- Nacos 2.3.2 正式釋出,修復重大 bug!
- iOS12公測3正式釋出:修復Bug提升穩定性iOS
- IOS下box-shadow的詭異bug的修復iOS
- 讓程式設計師少掉幾根頭髮的Facebook智慧bug修復神器程式設計師
- 機器人偽裝成人類在 GitHub 上為開源專案修復 bug機器人Github
- flexible.js 相容bug修復FlexJS
- Win10新正式版17134.319推送:修復多達53項BUGWin10
- 月光寶盒(vivo流量錄製回放平臺)正式對外開源
- 蘋果釋出iOS12.1.2全球正式版本:修復Bug為主蘋果iOS
- TensorFlow 1.7.0正式釋出,Bug修復和改進內容都在這裡了
- TensorFlow 1.8.0正式釋出,Bug修復和改進內容都在這裡了
- 蘋果iOS11.4正式版釋出:主要修復Bug和提升穩定性蘋果iOS
- 微軟推送Win10新正式版17134.137:修復影片播放bug問題微軟Win10
- TensorFlow1.8.0正式釋出,Bug修復和改進內容都在這裡了
- 微軟修復Bug的補丁產生了新的Bug微軟
- AI浪潮下12大開源神器介紹AI
- 一鍵fxxk,程式碼修復神器拯救你
- 熱修復和外掛化
- Android熱修復原理(一)熱修復框架對比和程式碼修復Android框架
- CSS之樣式無效BUG的修復CSS
- Mutter 3.35.1 已釋出–修復多項BUG
- Gitea 1.11.1 釋出了,修復了 14 個 bugGit
- 被冰封的 Bug:Fishhook Crash 修復紀實Hook
- 線上專案出BUG沒法除錯?這款阿里開源的診斷神器,自帶IDEA外掛真香!除錯阿里Idea
- 重磅丨科技教育公司“好未來”正式對外開源高效能PHP框架FendPHP框架
- BSN正式釋出開源外掛DDC保管箱
- 蘋果iOS 12.0.1更新,針對iPhone XS系列Bug作出最佳化和修復蘋果iOSiPhone
- Windows10系統三大正式版收穫累計補丁更新:修復大量bugWindows
- workman分散式部署遇到的bug以及修復方式分散式
- 部落格園主題美化中BUG修復方法
- Swoole v4.6.1 版本釋出,Bug 修復版本
- 微軟推送Win10 Mobile 15254.401正式版累積更新:主要修復bug和提升穩定性微軟Win10
- capt 正式開源APT
- Go 語言史詩級更新-迴圈Bug修復Go
- pear-admin-layui-main 4.0 admin.js bug 修復UIAIJS
- Unity開源技能編輯器Unity
- linux下修復磁碟損壞Linux
- laravel開發中遇到的問題與bug修復的一些總結Laravel