騰訊正式開源面向 Unity 項目的 Bug 修復神器 InjectFix

2020-12-15 開源中國

InjectFix是騰訊最新對外開源的Unity代碼邏輯熱修複方案,可實現在Unity線上客戶端內,不用迭代新版本,就能快速修復遊戲的線上bug。

先說幾個亮點:

  1. 直接在Unity工程上修改C#即可更新;老項目無需修改原有代碼即可使用;
  2. 更符合蘋果熱更新條款;
  3. 每個遊戲一份私有補丁格式,安全更有保障。

InjectFix經騰訊內部多個項目應用反饋十分良好,不僅能解決線上bug,還可以有效的提高日常開發效率,下面我們聊下這項目的前世今生。

熱更方案大亂鬥

所有支持iOS的熱更方案都有個共同點:更新後代碼都是解析執行。如果按其更新前是否解析執行,可以分為兩大類:

一類是某些模塊甚至整個遊戲,都一直解析執行。這是最傳統的方式,目前市面上所有主流方案(xLua,slua,tolua,ILRuntime,jsb等等)都支持這種方式。這種方式的特點:

  1. 或多或少都會有些侵入性:ILRuntime解析執行C#編譯後的程序集,在這些方案裡頭侵入性可能最小,但也需要對代碼重構,把要更新的邏輯拆到單獨程序集。各種非C#的腳本侵入性最大,一個已經完成的純C#項目要用意味著重寫。

ps:也有一種思路是通過一個C#轉XX腳本工具來實現C#編碼,解析執行,但如果你是一個已有項目想這麼轉一下,大概率是失敗的,除非你一開始就在用這方式在開發,碰到坑就避開,因為這類方案往往不是完整支持全部語法,支持的語法也不一定能完全一致。

  1. 基於性能,實現便利性等的考慮,一般遊戲有些地方要以原生的方式跑,這些原生跑的代碼出了bug這種方式是無能為力的。
  2. 如果使用的腳本是動態類型語言,還會帶來代碼維護困難的問題。
  3. 優點是可以新增功能,有的遊戲甚至可以做到一次下載,後續不用整包更新。但蘋果條款分析的章節可以看到,這也不一定是好事。

另外一類是以原生方式跑,如果有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支持重複加載補丁,新加載補丁會自動覆蓋上一個,這特性可以用來實現真機代碼邏輯實時修改。

(視頻地址:https://v.qq.com/x/page/v09240mo6ai.html?&ptag=4_7.2.5.22206_copy)

蘋果政策合規性

各熱更方案群的問的頻率最高的問題之一:這方案會不會導致我遊戲蘋果審核不通過。

讓我們看看蘋果的熱更新條款:

可以看到最新條款允許下載代碼解析執行,但前提是不能通過新增特性和功能來把程序改得(和審核時相比)面目全非。再看看通常被拒時的理由中的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使用簡單,小巧,合規且安全。即使你不打算用它來更新線上版本,只要你程序有原生部分,接入也能一定程度上提高開發效率,沒什麼拒絕它的理由,是吧?

相關焦點

  • 開源作業系統 Haiku 修復 2038 年 bug
    基於 BeOS 的開源作業系統 Haiku 項目修復了 2038 年 bug。
  • 從bug中學習:六大開源項目告訴你go並發編程的那些坑
    在go中,創建goroutine非常簡單,在函數調用前加go關鍵字,這個函數的調用就在一個單獨的goroutine中執行了;go支持匿名函數,讓創建goroutine的操作更加簡潔。另外,在並發編程模型上,go不僅僅支持傳統的通過共享內存的方式來通信,更推崇通過channel來傳遞消息:Do not communicate by sharing memory; instead, share memory by communicating.這種新的並發編程模型會帶來新類型的bug,從bug中學習,《Understanding Real-World Concurrency Bugs
  • 我為Windows 10 修復了一個 bug - OSCHINA - 中文開源技術交流社區
    本著對這個 bug 的好奇心,再加上 Windows 10 計算器是開源項目,Peter 認為解決這個問題應該不會太複雜,所以他希望親自找到 bug 並進行修復。他先在自己的電腦上測試看是否能復現,按照帖子的示例,在測試 7.31-12.31 的間隔天數時,計算器返回的結果是正確的 —— 「5個月」。
  • 網傳Epic Games啟動器莫名佔用CPU,官方承認出現bug正修復
    由於網絡反響越來越大,Epic開發團隊已表示將儘快發布修復程序。Epic Games的發布策略主管Sergey Galyonkin在Twitter上表示,公司已了解到情況,這是一個Bug。目前這個bug似乎是與AMD Ryzen的多線程技術有關,導致CPU進行大量運算,甚至可以拉高10~20°C,相當可觀。
  • 騰訊正式開源圍棋AI技術PhoenixGo
    PingWest品玩5月11日消息,微信團隊研發的PhoenixGo正式開源,這也是國內第一個開源的圍棋AI項目。PhoenixGo是騰訊微信翻譯團隊開發的人工智慧圍棋程序,旨在學習人工智慧前沿技術。從1月底開始,PhoenixGo以BensonDarr(暱稱「金毛」)的名義在騰訊野狐圍棋平臺上和全球頂尖職業棋手、圍棋愛好者及各路圍棋AI對弈,在4月底取得了200多連勝,並在福州2018人工智慧圍棋大賽上奪冠,引起了行業內廣泛關注。騰訊官方提到,一個好的圍棋AI,需要算法、工程實現、計算資源三者結合。
  • JavaFX 2012:徹底開源 - OSCHINA - 中文開源技術交流社區
    其中將JavaFX完全開源赫列榜首——在2011年的JavaOne大會上,Oracle已做出保證將開源整個JavaFX平臺。 以下是Richard Bair列出的2012年JavaFX 10大目標: 1.
  • KDE Plasma 5.1.2 發布,bug 修復版本
    KDE 發布了 Plasma 5 的 bug 修復版本,Plasma 5.1.2。此版本添加了這個月的新翻譯,還有 KDE 貢獻者發布的 bug 修復。這些 bug 修復非常經典,很小,但是很重要!Fix vertical aligment.
  • 3D 引擎 Unity 2019.2 正式發布 - OSCHINA - 中文開源技術交流社區
    刪除了舊的 .NET 3.5 等效腳本運行時,任何使用 .NET 3.5 等效腳本運行時的項目都將自動更新為 .NET 4.x 等效腳本運行時。Incremental Garbage Collection 作為在聯合 2019.1 中的一些平臺上的實驗性發布,現在支持除 WebGL 之外的所有平臺。
  • 12 個頂級 Bug 跟蹤工具
    它聽起來不怎麼有趣,但是如果想要提供良好的服務,除了建立一個 bug 跟蹤和修複流程,別無它途。跟蹤軟體會給你很大的靈活性,包含儘可能多的 bug 相關的的信息,從而安排優先級並修復它。它為開發和測試團隊提供了一個系統來跟蹤軟體開發、應用程式開發和部署中的 bug 修復和代碼變更。
  • 百度開源平臺上線,聚合百度開源項目
    百度公司近日上線「百度開源平臺」,該平臺主要用來展示百度的開源項目。 目前該平臺列出了10款百度公司的開源項目,分別有: Terminator:一款伺服器虛擬化解決方案 Itest:面向service接口的自動化測試工具
  • 騰訊雲聯合英特爾 美團等公布SuperEdge邊緣容器開源項目
    [PConline資訊]12月19日消息,今日下午,在2020年騰訊Techo Park開發者大會上,騰訊雲聯合英特爾、VMware威睿、虎牙、寒武紀、美團、首都在線,共同發布SuperEdge邊緣容器開源項目。
  • 騰訊雲聯合英特爾、美團等發布 SuperEdge 邊緣容器開源項目
    【TechWeb】12 月 19 日消息,今日下午,在 2020 年騰訊 Techo Park 開發者大會上,騰訊雲聯合英特爾、VMware 威睿、虎牙、寒武紀、美團、首都在線,共同發布 SuperEdge 邊緣容器開源項目。
  • 騰訊Node.js基礎設施TSW正式開源
    經過六年的迭代與沉澱,騰訊Tencent Server Web (以下簡稱TSW)這一公司級運維組件於今日正式開源。TSW是面向WEB前端開發者,以提升問題定位效率為初衷,提供雲抓包、全息日誌和異常發現的Node.js基礎設施。
  • 騰訊Tendis 正式開源:企業級分布式高性能 KV 存儲資料庫
    IT之家12月22日消息 近期,騰訊宣布企業級分布式高性能 KV 存儲資料庫 Tendis 正式開源。IT之家獲悉,Tendis 是騰訊互娛 CROS DBA 團隊 & 騰訊雲資料庫團隊自主設計和研發的分布式高性能 KV 存儲資料庫,兼容 Redis 核心數據結構與接口,可提供大容量、低成本、強持久化的資料庫能力,適用於兼容 Redis 協議、需要大容量且較高訪問性能的溫冷數據存儲場景。Tendis 目前已經被應用到騰訊內、外部大型項目中。
  • 推薦15 款最好的 Bug 跟蹤應用程式 - OSCHINA - 中文開源技術交流...
    而且,用戶總想標記未解決的問題,保證項目的進度等等。團隊會花費一部分的精力去跟蹤 bug ,並且找出問題所在,解決問題。如果你使用一個 bug 和問題跟蹤系統,那麼會得到更好的最終結果,除此之外,還能打打提高工作效率,加快項目的進度,更好的完成任務。在這裡,我們收集了最好的 15 款 bug 跟蹤應用程式,提供給用戶更舒適更方便的開發環境。
  • 騰訊員工中66%是研發,用C++最多,去年新寫12.9億行代碼
    在騰訊做研發是種怎樣的體驗?現在,「鵝廠」用官方數據給出了答案。今天(3月10日),騰訊發布《騰訊研發大數據報告》,披露了2019年騰訊在研發方面的核心數據。從騰訊研發工程師去年寫了多少代碼、最喜歡用什麼樣的語言、bug解決效率如何,以及領導是否寫代碼等等,應用盡有。
  • Yearning 1.0.0 正式版更新,SQL 審核自動化平臺
    Yearning SQL審核平臺 v1.0.0版本正式發布主要更新內容如下:新增sql
  • 騰訊推出HTML5開源專業在線圖像處理引擎
    這兩個圖像處理引擎都提供開原始碼下載。騰訊打造HTML5開源圖形處理引擎騰訊的AlloyPhoto界面比較親民,功能就如另一個在線圖像處理工具――美圖秀秀網頁版相類似,定位專業級的 AlloyImage