CrowdStrike“全球滅霸響指”事件后續(xù),德國 10% 企業(yè)更換安全供應商導致 1TB 數(shù)據(jù)泄露后,迪士尼宣布棄用 Slack 平臺合合信息啟信產(chǎn)業(yè)大腦攜手市北新區(qū)打造“一企一畫像”平臺,加速數(shù)字化轉(zhuǎn)型重慶:力爭今年智能網(wǎng)聯(lián)新能源汽車產(chǎn)量突破 100 萬輛,到 2027 年建成萬億級產(chǎn)業(yè)集群微信iOS最新版上線:iPhone用戶可在朋友圈發(fā)實況照片了蘋果有線耳機或?qū)⑼.a(chǎn)沖上熱搜!閑魚相關搜索量暴漲384%2024 vivo開發(fā)者大會官宣:OriginOS 5/自研藍河系統(tǒng)2降臨真·AI程序員來了,阿里云「通義靈碼」全面進化,全流程開發(fā)僅用幾分鐘東方甄選烤腸全網(wǎng)銷量及銷售額領先鴻蒙PC要來了 界面很漂亮!余承東:目前華為PC將是最后一批搭載Windows上半年中國AR/VR出貨23.3萬臺,同比下滑了 29.1%IDC:2024 上半年中國 AR / VR 頭顯出貨 23.3 萬臺,同比下滑 29.1%英特爾AI加速器Gaudi3下周發(fā)布,挑戰(zhàn)NVIDIA統(tǒng)治地位!大屏技術邂逅千年色彩美學!海信激光電視成為電影《只此青綠》官方合作伙伴OpenAI將最新AI模型o1擴展到企業(yè)和教育領域三星新專利探索AR技術新應用:檢測屏幕指紋殘留,提高手機安全性猛瑪傳奇C1:直播圖傳技術的革新者JFrog推出首個運行時安全解決方案,實現(xiàn)從代碼到云的全面軟件完整性和可追溯性亞馬遜推出一大波生成式 AI 工具,購物體驗全面升級機器人公司1X推出世界模型
  • 首頁 > 產(chǎn)經(jīng)新聞頻道 > 業(yè)界新聞

    環(huán)信IM Unity SDK 2.0正式發(fā)布,大大提升開發(fā)效率

    2021年08月25日 10:33:39   來源:中文科技資訊

      引言

      Untiy作為游戲引擎和內(nèi)容開發(fā)平臺,吸引了眾多游戲開發(fā)者,基于其開發(fā)的游戲更是不勝其數(shù)。具體請參見1。

    1.jpg

      環(huán)信作為領先的即時通訊云服務商,在游戲行業(yè)也進行了持續(xù)的探索和研發(fā)投入。在產(chǎn)品發(fā)布的早期(2015年)就推出了Unity SDK,幫助游戲開發(fā)者快速實現(xiàn)游戲場景下諸如世界頻道,游戲公會、組隊群聊,1對1私聊等功能,安全穩(wěn)定的服務也為游戲玩家?guī)砹藰O佳的實時溝通體驗。

      2021年第二季度,環(huán)信IM Unity SDK進行了重構改版,環(huán)信IM Unity SDK 2.0正式發(fā)布,主要改進包括如下:

      1、迭代更新,更加實用的API接口

      2、IM+Push增強功能的補全

      3、C#語言層面引入了版本7.0 – 9.0之后的一些新語法改進

      4、特別的,增加了PC端Unity Editor環(huán)境下編譯調(diào)試支持,大大提升了開發(fā)效率

      在過去的一段時間里,筆者也參與了相應的研發(fā)工作。在整個過程中,為了解決各種問題,不僅要到處翻閱資料,還要嘗試各種方法和參數(shù)組合。其間也經(jīng)歷了各種程序崩潰甚至系統(tǒng)崩潰,詭異的程序表現(xiàn)一次次讓開發(fā)人員束手無策,四處碰壁,當真像深夜里行走在迷宮之中,手里還拿著一個待破解的魔方。“此路不通,請繞行!”,是在一次次的嘗試后無奈的慨嘆和難舍的放棄。而一旦問題最后得到圓滿解決,又宛如飛入云端,以上帝視角俯瞰一片片迷宮,一切又顯得那么理所當然,繁復瑣細但又絲絲入扣,這樣的苦盡甘來也算是做程序員能享受到的巨大喜悅和滿足。

      不敢獨享,特記錄下一些心得供大家參考,也歡迎.NET平臺資深玩家批評指正。以下,Enjoy!

      開發(fā)概覽:非托管插件開發(fā)(Native/Unmanaged Plugin)

      Unity是基于Microsoft .Net Framework開發(fā)的游戲引擎2,它采用了開源的.NET Platform,并依賴此框架來實現(xiàn)跨硬件設備和運行時(操作系統(tǒng))的目標,也是所謂的”Write once, run anywhere”。在語言方面,Unity選擇C#作為主要的腳本編程語言,雖然.NET平臺本身支持的語言有很多種。

      進一步,Unity支持Mono和ILC2PP兩種腳本框架(Scripting Backends)。特別的,Unity Editor采用的是Mono腳本框架。

      一般的,游戲類庫開發(fā)者可以選擇直接用C#語言開發(fā),目標類庫可以實現(xiàn)基于.NET Framework基礎功能之上的高級功能,這類插件稱之為Managed Plugin(托管插件)。由于環(huán)信IM核心SDK已經(jīng)基于C++開發(fā),因此我們選擇另一種Native Plugin(本地插件)的方式,正是它把我們引向了迷宮之旅。兩種類型的Plugin介紹,參見3。

      不幸的是,Unity網(wǎng)站上關于Native Plugin的相關介紹少只又少,想要了解它的具體細節(jié)還要去參考Microsoft MSDN文檔。作為中規(guī)中矩的文檔介紹,微軟的文檔是合格的,但是,當你真正上手編程時就會發(fā)現(xiàn),這些遠遠不夠:下面記錄的一些坑點就很難在相應的文檔中得到直接的提示;而要通過Google大法,結合其他程序員留下的蛛絲馬跡,再加上自己不斷的調(diào)試來最終確認。

      在微軟文檔上下文中,Unity Native Plugin有個另外的名字:Unmanaged Plugin,即非托管插件。簡單來講,Managed Plugin生存在.NET Framework的運行時環(huán)境(類似于Java的JVM),而Unmanaged Plugin則生存在這個運行時環(huán)境之外,也即和運行時環(huán)境是兄弟的關系。如果你原本的類庫實現(xiàn)滿足微軟的COM(Component Object Model)規(guī)范,那自然最好是使用COM Interop4的互操作方式;而環(huán)信IM SDK本身是純C++實現(xiàn),因此采用了Platform Invoke5(簡稱P/Invoke)方式,本文剩下的內(nèi)容均是基于P/Invoke。

      下圖則概要描述了Managed和Unmanaged區(qū)域代碼之間互相操作的方式:

      更具體的,為了實現(xiàn)對于Unmanaged DLL function的調(diào)用,只需要簡單的4步6:

      1、確認DLL類庫中需要被操作的函數(shù);

      2、創(chuàng)建一個C#類來關聯(lián)被操作的這些函數(shù)(給函數(shù)穿上一個馬甲,以便集中管理和反復調(diào)用);

      3、使用DllImport標志在受管側(cè)(C#)定義函數(shù)原型;

      4、在受管側(cè)隨意調(diào)用相關非托管區(qū)域函數(shù)。

      上圖中,Standard marshalling service即負責將數(shù)據(jù)在兩個區(qū)域進行封裝/解封裝傳送(marshall/unmarshall),它主要定義了數(shù)據(jù)在兩個不同內(nèi)存區(qū)域進行拷貝(Copy)和引用(Reference)的規(guī)則7,而迷宮中的坑主要是和這些具體規(guī)則有關。

      坑王駕到之封送(Marshall/Unmarshall)中的那些坑

      坑一:sizeof(bool) = ?

      絕大多數(shù)的基本類型屬于Blittable Types8:如System.Byte, System.Single等。System.Boolean雖然不屬于Blittable types,但是Standard Marshalling Service默認將其轉(zhuǎn)換為1,2,4字節(jié)的內(nèi)存存儲,當其值為true時,其對應的值為1。如果你想當然的直接將System.Boolean映射到Unmanaged側(cè)的bool類型而不做特別處理的話,你并一定會理解碰到編譯或者運行時錯誤,但是如果你嚴格的測試每個字段是,會驚訝的發(fā)現(xiàn)這些bool值跟你想象的不盡相同:有時正確,有時錯誤。

      經(jīng)過調(diào)試跟蹤,動態(tài)打印sizeof(bool)來確認Unmanaged側(cè)bool類型數(shù)據(jù)長度后,你會發(fā)現(xiàn)System.Boolean默認會被保存為4個字節(jié)長度,而在macOS環(huán)境下(對于其它環(huán)境,需要自行認證),C++定義的bool其實只有一個字節(jié)。因此當你在Unmanaged側(cè)取bool值的時候,其實只讀取了System.Boolean的1/4個字節(jié)而已。而當你聲明了多個連續(xù)的System.Boolean/bool值時,可能在Unmanaged側(cè)讀取的這幾個bool值僅僅是第一個System.Boolean值的不同偏移字節(jié)而已。

      知道了原因,解決方案自然就出來了,在Managed側(cè)強制聲明System.Boolean字段封送到Unmanaged側(cè)時僅使用一個字節(jié):

      [MarshallAs(UnmanagedType.U1)]public bool TrueOrFalse;

      坑二:字節(jié)對齊

      對于C++開發(fā)者來說,可能知道當一個數(shù)據(jù)結構(class or struct)中的各字段在內(nèi)存中進行排列時,會按照一個設定的裝箱長度進行字節(jié)對齊,例如:

      struct MyStruct {

      int one;

      short two;

      int three;

      bool four;

      }

      假設在我們的平臺上,sizeof(int)=4, sizeof(short)=2, sizeof(bool)=1, 如果問你sizeof(MyStruct)=?,你可能會馬上做個加法得到答案,但是答案不一定對。It depends! 假設我們是按照4個字節(jié)對齊,這上面的結構體在內(nèi)存中實際排列如下圖:

      了解這個對于我們編碼有兩個意義:

      1、通過合理排列字段聲明順序來優(yōu)化存儲效率,內(nèi)存布局中不留空洞;

      2、MarshalAsAttribute支持Layout.Explicit來進行絕對定位,懂得了字節(jié)對齊可以配合Unmanaged側(cè)的內(nèi)存排列規(guī)則以保證字段長度映射正確,不然同樣會發(fā)生字段長度不一致帶來的困擾。

      坑三:如何避免Double Free

      Standard Marshalling Service/Interop marshaller總是試圖釋放Unmanaged側(cè)代碼分配的內(nèi)存9,這會帶來Double Free的問題,如果碰到這種問題,程序就會直接崩潰。

      引用資料中舉了以下例子:

      BSTR MethodOne (BSTR b) {

      return b;

      }

      如果這段代碼直接從Unmanaged側(cè)DLL中直接執(zhí)行,不會發(fā)生任何額外的內(nèi)存釋放;但是當你從Managed側(cè)調(diào)用這個方法時,b會被釋放兩次。

      而更讓人抓狂的是,并沒有相應的信息提示究竟是哪個指針,哪個字段被Double Free了,你唯一能做的就是一點點加代碼來驗證自己猜測。所以,嚴格來說,并沒有一個萬無一失的方案來避免Double Free,你唯一能做的就是通過測試來驗證結果(有點盲擰魔方的味道了)。

      有兩個基本的方法來解決Double Free的問題:

      1、按照官方文檔建議,在Unmanaged側(cè)通過使用CoTaskMemAlloc來分配內(nèi)存,通過此種方法分配的內(nèi)存,除非顯式調(diào)用了CoTaskMemFree方法(在Unmanaged側(cè)或者Managed側(cè)均可以調(diào)用),Interop Marshaller會嚴格保證不去釋放該內(nèi)存。使用這種方法可以靈活的在任意一側(cè)分配內(nèi)存,并在合適的時候在另一側(cè)釋放內(nèi)存。

      2、但上面這種方法貌似僅適用于Windows平臺,在macOS下沒有辦法使用(需要引用win32base.dll相關實現(xiàn))。在macOS下僅能通過在Mananged側(cè)調(diào)用Marshal.AllocCoTaskMem()方法分配內(nèi)存,并通過Marshal.FreeCoTaskMem()來在同一側(cè)進行釋放(按照此方法分配的內(nèi)存指針傳入Unmanaged側(cè)后,不要進行任何釋放即可)。另外有一個不太可靠的workaround是:在Unmanaged一側(cè)創(chuàng)建的內(nèi)存指針盡量通過IntPtr傳遞,并在可能的時候?qū)ο笾幸恍┲羔橆愋偷膶傩灾抵每,以避免Double Free的發(fā)生。

      坑四:virtual函數(shù)帶來的內(nèi)存布局變化

      vptr和vtable是C++的一個概念:當你定義的類型中有虛函數(shù)存在時,內(nèi)存對象的第一個位置會存放一個vptr指針,該指針指向vtable(虛函數(shù)表)。因此當你開始創(chuàng)建的自定義類型一開始沒有虛函數(shù)時(包括虛析構函數(shù)virtual ~MyClass()),一切運行正常。有一天你重構此類型,增加了一些虛函數(shù):DUANG,一切都崩塌了!原因就在于Unmanaged側(cè)內(nèi)存對象的排列規(guī)則變了,原有的對象字段都被新加入的vptr往后面移位了。此時可能你唯一能做的就是通過Layout.Explicit來手工對齊每一個字段新的位置。

      其它坑

      坑一:針對M1芯片編譯

      對于M1芯片的macOS系統(tǒng),編譯環(huán)信IM Unity SDK時候需要注意幾個問題:

      1、XCode編譯時需要Excluded Architecture中排除arm64架構(很奇葩的設置,不是應該排除x86嗎?)

      2、類庫的依賴解決:通過otool -L命令來確認相應的plugin依賴的類庫位置都正確(文件路徑下文件確實存在),如果相應文件不存在要手工拷貝文件到指定目錄:而新的macOS安全架構限制了往系統(tǒng)目錄下(如/usr/lib)進行任何改動,一個臨時的解決方法是通過install_name_tool工具主動修改類庫依賴路徑到另一個可以放置新文件的位置(如home目錄)。

      坑二:Delegate的正確使用姿勢

      如果Managed側(cè)的編程語言是C#,則Delegate是實現(xiàn)回調(diào)的重要手段。在Unmanaged側(cè)完成期望工作時回調(diào)一個FunctionPtr即可實現(xiàn)通用的回調(diào)模式,而此FunctionPtr正是對應到Managed側(cè)的Delegate。當你的Delegate綁定到一個類對象上時,你有兩種選擇:

      namespace ChatSDK {

      //delegate definition

      public void delegate OnMessageReceived(EMMessage message);

      public class MyDelegate {

      //Option 1: field

      public OnMessageReceived MyMessageReceived;

      //Option 2: instance method

      public void OnMessageReceived(EMMessage message)

      {

      ...

      }

      }

      //send delegate method to unmanaged side

      MyDelegate md = new();

      NativeMethods.SetOnMessageReceivedCallback(md.MyMessageReceived); //option 1

      NativeMethods.SetOnMessageReceivedCallback(md.OnMessageReceived); //option 2

      }

      看起來兩個方式都沒有問題,并且第二個方式看起來更順眼。但是這里隱藏著一個很深的坑,就是你選擇第二個方式的時候,如果你在回調(diào)方法實現(xiàn)中采用this.xxx方式引用時,你會發(fā)現(xiàn)this = null!這是因為當你使用這種方式傳遞一個對象的方法作為回調(diào)方法指針時,其實已經(jīng)丟失了Delegate.Target(也就是this)屬性。而通過第一種方式傳遞的是一個對象的屬性/字段,它和對象本身的綁定是不會在傳遞過程中丟失的。

      至于該Delegate字段的定義可以在此類的構造函數(shù)中通過以下方式實現(xiàn):

      ...

      public MyDelegate() {

      MyMessageReceived = (EMMessage message) => { ... }

      }

      ...

      參考資料

      1、List of Unity Games: https://en.wikipedia.org/wiki/List_of_Unity_games

      2、Unity and .NET: https://docs.unity3d.com/Manual/overview-of-dot-net-in-unity.html

      3、Unity Scripting-Plugins: https://docs.unity3d.com/Manual/Plugins.html

      4、COM Interop: https://docs.microsoft.com/en-us/dotnet/standard/native-interop/cominterop

      5、Platform Invoke: https://docs.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke

      6、如何調(diào)用Unmanaged DLL Functions:https://docs.microsoft.com/en-us/dotnet/framework/interop/consuming-unmanaged-dll-functions

      7、Interop Marshalling:https://docs.microsoft.com/en-us/dotnet/framework/interop/interop-marshaling

      8、Blittable Types: https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types

      9、Double Free: https://docs.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-behavior

      文章內(nèi)容僅供閱讀,不構成投資建議,請謹慎對待。投資者據(jù)此操作,風險自擔。

    [No. X016-1]
    分享到微信

    即時

    TCL實業(yè)榮獲IFA2024多項大獎,展示全球科技創(chuàng)新力量

    近日,德國柏林國際電子消費品展覽會(IFA2024)隆重舉辦。憑借在核心技術、產(chǎn)品設計及應用方面的創(chuàng)新變革,全球領先的智能終端企業(yè)TCL實業(yè)成功斬獲兩項“IFA全球產(chǎn)品設計創(chuàng)新大獎”金獎,有力證明了其在全球市場的強大影響力。

    新聞

    敢闖技術無人區(qū) TCL實業(yè)斬獲多項AWE 2024艾普蘭獎

    近日,中國家電及消費電子博覽會(AWE 2024)隆重開幕。全球領先的智能終端企業(yè)TCL實業(yè)攜多款創(chuàng)新技術和新品亮相,以敢為精神勇闖技術無人區(qū),斬獲四項AWE 2024艾普蘭大獎。

    企業(yè)IT

    重慶創(chuàng)新公積金應用,“區(qū)塊鏈+政務服務”顯成效

    “以前都要去窗口辦,一套流程下來都要半個月了,現(xiàn)在方便多了!”打開“重慶公積金”微信小程序,按照提示流程提交相關材料,僅幾秒鐘,重慶市民曾某的賬戶就打進了21600元。

    3C消費

    “純臻4K 視界煥新”——愛普生4K 3LCD 激光工程投影

    2024年3月12日,由愛普生舉辦的主題為“純臻4K 視界煥新”新品發(fā)布會在上海盛大舉行。

    研究

    2024全球開發(fā)者先鋒大會即將開幕

    由世界人工智能大會組委會、上海市經(jīng)信委、徐匯區(qū)政府、臨港新片區(qū)管委會共同指導,由上海市人工智能行業(yè)協(xié)會聯(lián)合上海人工智能實驗室、上海臨港經(jīng)濟發(fā)展(集團)有限公司、開放原子開源基金會主辦的“2024全球開發(fā)者先鋒大會”,將于2024年3月23日至24日舉辦。