• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            戰(zhàn)魂小筑

            討論群:309800774 知乎關(guān)注:http://zhihu.com/people/sunicdavy 開源項(xiàng)目:https://github.com/davyxu

               :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              257 隨筆 :: 0 文章 :: 506 評論 :: 0 Trackbacks

            2018年11月30日 #

            近期在游戲demo中試驗(yàn)ECS, 深入研究Unity官方的ECS框架和第三方Entitas框架, 分享下使用ECS的心得。


            Unity在2018版中加入了ECS系統(tǒng), 但處于小白鼠階段。默認(rèn)不是Unity的一部分, 需要手動(dòng)下載代碼并導(dǎo)入Packages(新特性)。官方提供海豚例子, 但除此之外例子和資料非常少。所以完全無法,也不敢在demo中貿(mào)然引入這種系統(tǒng),所以放棄官方ECS系統(tǒng)。


            第三方的Entitas(https://github.com/sschmid/Entitas-CSharp)ECS框架從2015年就開始在各地演講中介紹。整體框架基于代碼生成, 能解決一部分的代碼爆炸問題, 而且性能也要好一些。例子,介紹非常豐富,例子雖然基于不同版本的Entitas,特性支持和最新版差不太多, 只是寫法有細(xì)微差異, 對于理解來說無礙。


            經(jīng)過1~2天的改造, 終于將demo從傳統(tǒng)Unity寫法改造為ECS標(biāo)準(zhǔn)寫法,新增了46個(gè)文件, 而傳統(tǒng)邏輯一共只有16個(gè)文件,大概對比下ECS的特點(diǎn)和差異。


            Entitas的ECS系統(tǒng)

            1. 本來在一個(gè)對象中添加一個(gè)類字段的過程,ECS需要添加一個(gè)類代表Component,并且代碼生成。

            這個(gè)字段一般用于描述對象的資源,處理顯示的GameObject, 表示對象的類型等。


            2. 本來一個(gè)對象的業(yè)務(wù)邏輯處理過程直接用方法解決的, ECS需要新加一個(gè)System,而操作對象需要使用Filter或Group查詢獲得。


            3. 一系列的操作, 需要拆分為多個(gè)System和Component拆分處理。如果System順序不對, 會(huì)造成一些詭異的bug。


            4. Component不僅僅是Model承載體, 也可以是參數(shù)的數(shù)據(jù)結(jié)構(gòu)。參數(shù)Component通過Entity傳遞到System處理。 例如: 通過ECS創(chuàng)建一個(gè)方塊的過程,使用CreateTileComponent,包含創(chuàng)建Tile的位置, 創(chuàng)建Entity并添加CreateTileComponent, 在CreateTileSystem中處理就創(chuàng)建了Tile,處理完成時(shí), 需要將傳入的Entity.Destroy掉。


            6. Entity上修改Component的過程, 會(huì)觸發(fā)事件。修改的過程需要使用RelaceXXX,XXX表示組件名。組件可以頻繁修改, 不用擔(dān)心添加和刪除組件過程的性能, Entitas底層處理性能只相當(dāng)于指針賦值的性能。


            ECS像什么?

            1. ECS中的System類似觸發(fā)器系統(tǒng)(Event-Condition-Action),其中,Event對應(yīng)Entitas的GetTrigger+Collector,表示觸發(fā)事件。Condition對應(yīng)Filter表示在事件來源對象中找到需要的對象。 Action對應(yīng)Execute,表示實(shí)際的操作。

            2. ECS中的Component類似不用lua擴(kuò)展的Redis或者不用存儲(chǔ)過程的MySQL, 純粹純數(shù)據(jù), 而不能對數(shù)據(jù)有任何封裝操作。沒有l(wèi)ua和存儲(chǔ)過程支持的db寫起來還是比較費(fèi)勁的,但ECS就是那么的純。


            3. ECS中的Entity很尷尬,因?yàn)镃omponent是按類別連續(xù)存儲(chǔ)的以保證性能。 邏輯又需要Entity組合成邏輯需要的復(fù)合對象。 兩邊都要照顧,所以這種設(shè)計(jì)就讓代碼量巨增,可讀性下降。



            ECS企圖用一套框架滅掉設(shè)計(jì)模式

            1. 單件(Singleton)在Entitas用Unique標(biāo)簽標(biāo)記Component, 在Context中就是唯一的, 其實(shí)也就是Singleton。


            2. ECS干掉了傳統(tǒng)的工廠模式,底層統(tǒng)一對對象(Entity)和屬性(Component)統(tǒng)一管理。需要按Component中的值找回Entity時(shí), 可以使用EntityIndex。


            3. Entity攜帶不同的組件時(shí),整個(gè)創(chuàng)建和銷毀過程被記錄并恢復(fù),其實(shí)就是Command模式


            ECS適合做UI框架(類似MVVM,MVC,MVP)么?

            ECS不是專用的UI框架,但是可以對不同系統(tǒng)和數(shù)據(jù)間解耦。傳統(tǒng)代碼中數(shù)據(jù)修改后的Callback,ECS也可以用Listener做, 但Listener因?yàn)槟鼙4鏀?shù)據(jù), 就需要用Component保存。 所以你需要面對的是,一個(gè)Button,響應(yīng)創(chuàng)建一個(gè)參數(shù)用的Component和System,還要為數(shù)據(jù)改變寫一套ListenComponent和Listener處理的System,酸爽吧?


            Minecraft適合ECS來做么?

            可以,性能應(yīng)該能提升不少,但是代碼會(huì)更繁瑣,特別像Java這種啰嗦語言配上ECS這種啰嗦框架,估計(jì)代碼量翻翻還是很輕松的。MC屬于特殊類型的游戲,適合特殊領(lǐng)域特別優(yōu)化,也就是專門為方塊做出特別的設(shè)計(jì)來做優(yōu)化。ECS屬于通用框架,即便性能OK,但是代碼未必能有良好的可讀性。


            體量小的游戲適合用ECS來做么?

            可以,但不建議。特別是只有幾個(gè)人維護(hù)的工程,貿(mào)然上ECS系統(tǒng),會(huì)讓系統(tǒng)變的極為復(fù)雜。當(dāng)然你會(huì)說,如果開發(fā)到后期,傳統(tǒng)開發(fā)模式會(huì)導(dǎo)致代碼會(huì)亂,ECS會(huì)好些吧。掌握ECS也不是一天兩天的事情,不熟悉ECS的程序員設(shè)計(jì)出來的系統(tǒng)獲得的優(yōu)勢可能還不如用傳統(tǒng)設(shè)計(jì)方法好呢。

            架構(gòu)解決的是人的問題, 人都有問題,用什么框架都沒辦法。


            到底什么項(xiàng)目適合用ECS?

            1. 大量的小個(gè)體不斷的生成和銷毀以及顯示,例如: 攻城戰(zhàn)中,要體現(xiàn)每個(gè)角色的移動(dòng),戰(zhàn)斗。

            2. 多于5個(gè)人編寫核心戰(zhàn)斗邏輯?;ハ鄥f(xié)作和模塊切分,需要一個(gè)大家都能信服的框架,ECS可以選擇。


            P.S.

            不要造ECS的輪子!

            很多同學(xué)看了ECS基本原理,在沒有深入使用過任何ECS系統(tǒng)時(shí)馬上操刀造輪子。ECS系統(tǒng)確實(shí)看起來簡單。實(shí)際造下來你會(huì)發(fā)現(xiàn),性能非常糟糕以及不知道一些邏輯如何用ECS來解決。


            總結(jié):

            1. ECS確實(shí)為性能而生,沒有并發(fā)加持性能的ECS都是耍流氓,要快就要快到極致。

            2. Unity中,ECS并發(fā)能擴(kuò)展CPU的利用率,但是GPU的性能依然還是DrawCall優(yōu)化那一套,別期望ECS會(huì)顛覆Unity,性能也不會(huì)快到飛起,關(guān)鍵還是要看具體的項(xiàng)目和人。

            3. ECS是萬能框架,但不全能。傳統(tǒng)架構(gòu)和設(shè)計(jì)思想也不是一無是處,熟啥用啥,怎么快怎么來!

            無恥的廣告鏈接,請各位支持

            《Go語言從入門到進(jìn)階實(shí)戰(zhàn)(視頻教學(xué)版)》(徐波)【摘要 書評 試讀】- 京東圖書

            posted @ 2018-11-30 18:01 戰(zhàn)魂小筑 閱讀(3263) | 評論 (0)編輯 收藏

            2018年8月29日 #

            為了編寫基于cellnet的新一代游戲服務(wù)器框架,最近深入研究微服務(wù),ServiceMesh等概念。研究過程中對Web和游戲兩種服務(wù)器架構(gòu)設(shè)計(jì)有一些心得,編寫并記錄下來。(下文中,Game表示游戲服務(wù)器,Web表示W(wǎng)eb服務(wù)器) ``
            狀態(tài)緩存
            所謂狀態(tài)緩存,就是在內(nèi)存而非專業(yè)數(shù)據(jù)緩存服務(wù)器(如redis)中保存和處理邏輯數(shù)據(jù),手動(dòng)編寫此過程較為繁瑣但是效率較高,但隨著狀態(tài)邏輯復(fù)雜性和并發(fā)、擴(kuò)容問題提出,狀態(tài)同步會(huì)變得越來越復(fù)雜。
            Game:
            強(qiáng)交互性的服務(wù)器類型需要在服務(wù)器做緩存,邏輯編寫也較為容易,無需處理事務(wù)并發(fā)問題。例如:組隊(duì),匹配,戰(zhàn)斗邏輯。服務(wù)器不能隨意重啟。
            弱交互性的服務(wù)器類型可配合redis做成無狀態(tài)服務(wù)器,例如:養(yǎng)成,技能升級,領(lǐng)取物品等。服務(wù)器隨時(shí)支持重啟。
            游戲服務(wù)器為了提高性能,早期所有服務(wù)器都是使用狀態(tài)緩存寫法編寫,特別是MMORPG這類強(qiáng)交互的游戲服務(wù)器尤為嚴(yán)重。
            Web:
            均為無狀態(tài)服務(wù)器,弱交互。使用事務(wù)方式處理并發(fā)邏輯,例如:交易,下單等。
            推送,單獨(dú)發(fā)送
            這里提到的所謂推送,單獨(dú)發(fā)送是與RPC區(qū)別的通訊方法。RPC要求請求必須有回應(yīng)。而推送單獨(dú)發(fā)送則更像是通知和廣播,無需目的方返回任何消息。
            Game:
            找到服務(wù)器的Session,直接Send
            通過中轉(zhuǎn)服務(wù)器,或稱為中心服務(wù)器進(jìn)行注冊/廣播
            客戶端的model數(shù)據(jù)需要更新時(shí),服務(wù)器會(huì)主動(dòng)推送消息。
            游戲服務(wù)器沒有嚴(yán)格的RPC設(shè)計(jì)需求,推送和單獨(dú)發(fā)送較Web服務(wù)器更多。而且游戲服務(wù)器多使用長連接,所以主動(dòng)推送也比Web服務(wù)器來的方便一些。
            Web:
            將推送做成專有的服務(wù),并做排隊(duì)和并發(fā)處理。
            可用性
            聽說過游戲停服更新,支付寶服務(wù)器在刷二維碼時(shí)停服了可一定被罵慘吧。Web對服務(wù)器高可用性要求很高,游戲雖然也注重服務(wù)器穩(wěn)定性和可用性,但是由于版本迭代更新頻繁,停服更新反而能獲得玩家接受。
            Game:
            游戲?qū)捎眯砸蟛桓摺?/span>
            游戲大版本更新時(shí)需要停服更新。支持熱更新技術(shù)的服務(wù)器(例如Erlang,Skynet)僅使用熱更新修復(fù)bug,很少直接更新新版本。
            不是所有的游戲服務(wù)器支持動(dòng)態(tài)添加服務(wù)器。
            Web:
            極高的可用性,服務(wù)不允許停服更新,使用藍(lán)綠及灰度方式更新服務(wù)器。
            隨時(shí)可以橫向擴(kuò)展服務(wù)器,提高服務(wù)器容量和承載。
            連接及傳輸
            均使用TCP傳輸協(xié)議,游戲服務(wù)器注重性能,自有協(xié)議及二進(jìn)制協(xié)議使用較多。
            Web注重兼容和接口友好,使用JSON格式較多。
            Game:
            使用長連接,需要從邏輯層維護(hù)連接狀態(tài)及處理服務(wù)器不在線情況
            使用自有封包格式,大部分使用protobuf或二進(jìn)制流格式。
            Web:
            微服務(wù)大部分使用短連接,grpc支持http2長連接
            使用json編碼方便調(diào)試和版本兼容。
            流量限制
            人數(shù)多了,任何服務(wù)器都扛不住,流量限制和登入限制能有效保護(hù)服務(wù)器穩(wěn)定。
            Game:
            單服有人數(shù)限制,可以通過GM后臺(tái)設(shè)置擋墻,超過無法進(jìn)入
            Web:
            限流器中間件,可以精確到服務(wù)控制流量
            斷流,防止雪崩
            Game:
            游戲沒有,也不需要這種概念,游戲請求不會(huì)突然升高,即便有,也通過GM后臺(tái)人為控制
            Web:
            斷流器中間件
            服務(wù)發(fā)現(xiàn)
            如何找到服務(wù)器地址。
            服務(wù)有變化時(shí),通過Watch系統(tǒng)通知訂閱者更新本地緩存
            服務(wù)器沒有變化時(shí),使用本地緩存找到服務(wù)地址
            Game:
            游戲服務(wù)器互相依賴復(fù)用只在很小的范圍內(nèi),因此無需在不同語言不同進(jìn)程服務(wù)間獲得地址,大部分在配置文件中填寫各服務(wù)的IP及地址即可互相訪問。
            早期游戲自己編寫服務(wù)器狀態(tài)及地址發(fā)現(xiàn)服務(wù)。
            有用redis做服務(wù)發(fā)現(xiàn)
            Web:
            使用服務(wù)發(fā)現(xiàn)系統(tǒng),分布式部署。無需依賴配置文件
            網(wǎng)關(guān)需求
            Game:
            網(wǎng)關(guān)處理客戶端上下線通知,心跳,維持連接,轉(zhuǎn)發(fā),廣播上下行封包
            Web:
            根據(jù)請求地址路由,無上下線概念,無心跳。廣播通過消息推送系統(tǒng)完成
            由于筆者從事游戲行業(yè),對Web服務(wù)器概念在逐漸熟悉中,若有錯(cuò)誤和不足請各位大佬指出。
            本人新書《Go語言從入門到進(jìn)階實(shí)戰(zhàn)》,生動(dòng)的語言,例子帶有各種彩蛋,輕松了解Go語言特性,更有cellnet框架剖析解密
            https://search.jd.com/Search?keyword=go%E8%AF%AD%E8%A8%80%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E8%BF%9B%E9%98%B6%E5%AE%9E%E6%88%98&enc=utf-8&suggest=1.def.0.V02&wq=Go%E8%AF%AD%E8%A8%80%E4%BB%8E&pvid=145d55a92cab4b07b71326f8beb1700b
            posted @ 2018-08-29 11:16 戰(zhàn)魂小筑 閱讀(2686) | 評論 (0)編輯 收藏

            2017年12月19日 #

            嵌套git庫的管理
            使用git作為源代碼管理時(shí),經(jīng)常需要在一個(gè)git代碼庫中從外網(wǎng)獲取新的git庫,也就是在git庫下嵌套另外一個(gè)git庫。而同時(shí)維護(hù)兩個(gè)git庫的過程就變的非常復(fù)雜。
            submodule的弊端
            常見的做法是使用git 提供的submodule功能。但submodule的管理嵌套git庫的結(jié)果往往不是我們期望的結(jié)果。假設(shè)有一個(gè)git庫叫project,在project的某個(gè)子目錄下還包含一個(gè)叫3rd的目錄,是另外一個(gè)git庫。
            D:.
            └─project
            └─3rd
            mytext.txt
            假設(shè)甲和乙都取了project的代碼,由于時(shí)間差異,甲通過submodule取到V1版,而乙取到了V2版本。甲乙同時(shí)在不同的3rd庫下進(jìn)行開發(fā),勢必會(huì)造成不同的運(yùn)行結(jié)果。也許你認(rèn)為可以在獲取submodule時(shí)指定版本,但這個(gè)獲取過程很難控制。
            一般說來,第三方庫應(yīng)由主程序進(jìn)行更新及維護(hù),一般情況下,項(xiàng)目沒有特殊需求時(shí),不會(huì)隨便更新第三方庫到最新版本,因此submodule更新嵌套的git庫并不是理想的解決方案。
            嵌套git庫的修改可見性
            前面的例子中,project git庫下的3rd的git庫中如果有文件發(fā)生修改,此時(shí)在project目錄下,使用sourceTree等git管理工具無法識別3rd下的文件修改。
            也就是說,無法將3rd下的修改提交到project的git庫中。
            終極解決方案
            此時(shí),將3rd目錄下的.git目錄暫時(shí)移出project和3rd目錄,此時(shí),在project目錄用sourceTree查看時(shí),將可以看到3rd下的文件修改,提交修改到project庫中。再將剛移出去的3rd的.git目錄移回3rd目錄下,在3rd目錄下使用git status,將可看到3rd目錄的修改,提交時(shí),會(huì)將修改提交到3rd目錄。
            posted @ 2017-12-19 22:04 戰(zhàn)魂小筑 閱讀(3530) | 評論 (0)編輯 收藏

            2017年11月27日 #

            越來越多的人選擇用Mac或者Linux環(huán)境進(jìn)行跨平臺(tái)項(xiàng)目開發(fā)。但是仍然有大部分人習(xí)慣于在Windows環(huán)境下進(jìn)行開發(fā),畢竟Windows在各方面使用還是較為方便,特別像文件版本管理(Git,SVN等)

            在跨平臺(tái)下開發(fā)游戲或軟件,就需要有一套方便的自動(dòng)化工具。Windows下需要使用批處理,雖然有PowerShell加持,但這東西學(xué)了也不靠譜,只有一個(gè)平臺(tái)能用。大家還是習(xí)慣Linux Shell。連Mac平臺(tái)都可以用Shell,Windows上要做自動(dòng)化腳本就顯得非常尷尬。

            我曾經(jīng)在項(xiàng)目中使用go編寫了一套將配置轉(zhuǎn)為批處理和Linux Shell的工具。使用過程較為復(fù)雜,但是能跨平臺(tái)進(jìn)行表格導(dǎo)出和協(xié)議編譯等工作。

            但是,這個(gè)工具還是需要對不同的平臺(tái)編寫多套模板進(jìn)行代碼生成,非常繁瑣。如果有一套跨平臺(tái)的Shell,編寫一次就可以跨平臺(tái)運(yùn)行那該多好。

            查閱資料后,一共有幾個(gè)方案:

            1. 使用Python作為自動(dòng)化工具 這個(gè)方案其實(shí)就是使用python把批處理和Shell干的事情用代碼來解決。但前提是要重新學(xué)習(xí)Python,也需要一部分熟悉簡單的Python語法,人為學(xué)習(xí)成本較高,也比較費(fèi)事。

            2. 自己編寫一套獨(dú)立的自動(dòng)化工具 這個(gè)方案需要長時(shí)間的適配過程,差什么指令補(bǔ)什么指令,對項(xiàng)目進(jìn)度有一定干擾。

            3. 自己編寫Linux Shell的解釋器 這個(gè)就更難了,要做到100%兼容,基本不可能。

            1. 使用Cygwin和Mingw 需要一個(gè)微型運(yùn)行時(shí)進(jìn)行Linux Shell的解釋,msys大概是18M左右,可行性較高。

            在研究Cygwin和Mingw如何整合的過程中,我誤操作點(diǎn)擊了一個(gè)sh后綴的Linux Shell,這是我希望讓Mingw運(yùn)行的Shell。結(jié)果呢,sh后綴的文件居然能在Windows下運(yùn)行。我馬上編寫了一系列的例子,發(fā)現(xiàn)幾乎完全兼容常用的Shell指令。 經(jīng)過研究,我發(fā)現(xiàn)Windows下能運(yùn)行sh文件是由Git自帶的msys2提供的。MSYS2 (Minimal SYStem 2, http://www.msys2.org/) 是一個(gè)MSYS的獨(dú)立改寫版本,主要用于 shell 命令行開發(fā)環(huán)境。同時(shí)它也是一個(gè)在Cygwin (POSIX 兼容性層) 和 MinGW-w64(從"MinGW-生成")基礎(chǔ)上產(chǎn)生的,追求更好的互操作性的 Windows 軟件。

            那就是說,只要安裝了Git(https://git-scm.com/),那么就可以在Windows下直接運(yùn)行Linux Shell,只需要將文件后綴命名為sh即可。

            問過周邊友人是否知道這一功能,都說知道,只是沒有廣播而已,害我研究很久……

            posted @ 2017-11-27 15:15 戰(zhàn)魂小筑 閱讀(5404) | 評論 (3)編輯 收藏

            2017年7月6日 #

            本文主要研究游戲服務(wù)器帶狀態(tài)的熱更新需求 http的無狀態(tài)熱更新需求已經(jīng)有成熟方案, 故不在本文描述范圍

            基本概念

            • Golang的熱更新采用什么機(jī)制?

              使用go1.8提供的plugin包機(jī)制實(shí)現(xiàn)

            • plugin包本身設(shè)計(jì)的目的是熱更新么?

              plugin包其實(shí)只是支持將代碼分別編譯為多個(gè)動(dòng)態(tài)庫,動(dòng)態(tài)加載后運(yùn)行 并不能完全支持類似C/C++的動(dòng)態(tài)庫方式處理代碼

            • 帶狀態(tài)的進(jìn)程熱更新的基本概念及范圍是什么?

              數(shù)據(jù)部分(model)不更新, 只更新邏輯部分(函數(shù))

            • 表格和配置更新算熱更新么?

              算, 但不是本文描述范圍

            • 熱更新能在windows上使用么?

              不支持

            代碼及結(jié)構(gòu)

            • 我能將原來一個(gè)exe的代碼編譯為so提供給plugin使用么?

              可以, 但是必須仍然保留main包作為插件入口, 并在main包內(nèi)添加提供給plugin調(diào)用入口.

            • 如果動(dòng)態(tài)庫中沒有main包, 編譯出的so能用么?

              不能, 包必須包含main, 否則輸出的是.a的文件, plugin包加載會(huì)報(bào)錯(cuò)

            • 動(dòng)態(tài)庫中, 非main包的的代碼修改能做熱更新么?

              不能!(崩潰了吧, 我提了一個(gè)issue: https://github.com/golang/go/issues/20554)

              如果確實(shí)做了修改, 底層會(huì)報(bào)錯(cuò): plugin was built with a different version of package

              解決方法: 修改plugin包底層實(shí)現(xiàn)并重新編譯 打開runtime/plugin.go, 注釋以下代碼 for _, pkghash := range md.pkghashes { if pkghash.linktimehash != *pkghash.runtimehash { return "", nil, pkghash.modulename } } 執(zhí)行/usr/local/go/run.bash 重編譯+測試

            • 代碼中哪些可以被更新? 方法可以被更新么? 閉包呢?

              只能更新?lián)碛徐o態(tài)地址的結(jié)構(gòu).例如: 包級別函數(shù)(類似于靜態(tài)函數(shù))

              例如: svc_0.so里有一個(gè)Foo函數(shù), svc_1.so修改了Foo函數(shù)實(shí)現(xiàn), 熱更新可實(shí)現(xiàn)

              閉包=函數(shù)+捕獲變量, 實(shí)際上是一個(gè)動(dòng)態(tài)結(jié)構(gòu), 沒有靜態(tài)地址, 無法被更新

              各種包級別變量, 結(jié)構(gòu)體變量, 結(jié)構(gòu)體方法, 局部變量均不能被熱更新, 但是變量值不會(huì)被影響

              新增結(jié)構(gòu)可以被運(yùn)行

            • 使用結(jié)構(gòu)體方法調(diào)用了包級別函數(shù), 包級別函數(shù)能被更新么?

              可以, 雖然方法不能被更新, 但方法被調(diào)用的包級別函數(shù)的地址是固定的, 所以可以被熱更新

            • init包初始化函數(shù)能用么? 能被熱更新么?

              官方這樣描述:

              When a plugin is first opened, the init functions of all packages not already part of the program are called. The main function is not run. A plugin is only initialized once, and cannot be closed

              插件第一次被打開時(shí), 其關(guān)聯(lián)的, 沒有成為程序的一部分的所有的包的init函數(shù)將被調(diào)用. 插件的main函數(shù)不會(huì)被調(diào)用. 插件只會(huì)被初始化一次, 不能被關(guān)閉

              因此, 需要手動(dòng)將init函數(shù)改成自己的函數(shù), 統(tǒng)一在so的main包里調(diào)用

            編譯

            • 如何編譯獲得plugin包支持的動(dòng)態(tài)庫

              SVCNAME=$1 SVCVER=$2 TIMESTAMP=`date '+%Y%m%d_%H%M%S'` go build -v -buildmode=plugin --ldflags="-pluginpath=${SVCNAME}_${TIMESTAMP}" -o ${SVCNAME}_${SVCVER}.so ${SVCNAME}

              -buildmode=plugin是重要參數(shù)

              --ldflags里的-pluginpath的作用是: 每次編譯的內(nèi)部識別路徑都是不同的, 避免重復(fù)加載的警告

              參考: https://github.com/golang/go/issues/19004

            posted @ 2017-07-06 12:47 戰(zhàn)魂小筑 閱讀(8537) | 評論 (0)編輯 收藏

            2017年4月20日 #

            使用Visual Studio Code調(diào)試Golang工程

            關(guān)鍵字

            • 最簡單的調(diào)試攻略
            • 多項(xiàng)目調(diào)試, 適用個(gè)人開發(fā)和項(xiàng)目開發(fā)
            • 無需修改系統(tǒng)環(huán)境變量

            準(zhǔn)備VSCode

            在官網(wǎng)下載最新版的VSCode:

            https://code.visualstudio.com/

            安裝Golang插件

            • 打開擴(kuò)展面板

              VSCode->查看->擴(kuò)展

            • 找到Go插件 在搜索框里輸入Go, 找到第二行寫有 Rich Go language support for Visual Studio Code的插件, 點(diǎn)擊安裝

              注意不是排名最高的

            • 重啟編輯器

            配置啟動(dòng)項(xiàng)

            • 打開調(diào)試面板

              VSCode->查看->調(diào)試

            • 添加調(diào)試目標(biāo)

              在"沒有調(diào)試"的下拉框中點(diǎn)擊"添加配置.."

            • 添加目標(biāo)調(diào)試配置

              例子:

              {
                  "version": "0.2.0",
                  "configurations": [
                      {
                          "name": "Launch",
                          "type": "go",
                          "request": "launch",
                          "mode": "debug",
                          "remotePath": "",
                          "port": 2345,
                          "host": "127.0.0.1",
                          "program": "${fileDirname}",
                          "env": {
                              "GOPATH":"D:/Develop/vscodegolang"
                          },
                          "args": [],
                          "showLog": true
                      }
                  ]
              }

            其中: "port", "host"都是go插件自動(dòng)生成的

            "env"為設(shè)置環(huán)境變量, 設(shè)置為你的工程目錄就可以(包含bin, src的文件夾)

            準(zhǔn)備調(diào)試插件

            此時(shí)找到main.go按F5, 會(huì)報(bào)錯(cuò)提示:

            Failded to continue:"Cannot find Delve debugger. Install from https://github.com/derekparker/delve & ensure it is in your "GOPATH/bin" or "PATH"

            我們使用go命令行編譯調(diào)試器

            go get github.com/derekparker/delve/cmd/dlv

            將dlv調(diào)試器放在GOPATH(工程目錄)的bin目錄下

            開始調(diào)試

            選中要調(diào)試的main.go, 點(diǎn)擊F5, 既可以開始調(diào)試

            調(diào)試快捷鍵和Visual Studio系一致

            • F9 切換斷點(diǎn)
            • F10 Step over
            • F11 Step in
            • Shift+F11 Step out

            注意點(diǎn)

            • 某些結(jié)構(gòu)體成員無法直接顯示時(shí), 可以直接選中變量名, 添加到監(jiān)視, 或者右鍵點(diǎn)擊: "調(diào)試:求值"

            多項(xiàng)目調(diào)試

            在launch.json中可以添加多組調(diào)試入口, 通過調(diào)試面板中選中對應(yīng)的配置開啟不同目標(biāo)的調(diào)試

            {
                "version": "0.2.0",
                "configurations": [
                    {
                        "name": "client",
                        "type": "go",
                        "request": "launch",
                        "mode": "debug",
                        "remotePath": "",
                        "port": 2345,
                        "host": "127.0.0.1",
                        "program": "${fileDirname}",
                        "env": {
                            "GOPATH":"D:/Develop/vscodegolang"
                        },
                        "args": [],
                        "showLog": true
                    },
            
                    {
                        "name": "server",
                        "type": "go",
                        "request": "launch",
                        "mode": "debug",
                        "remotePath": "",
                        "port": 2345,
                        "host": "127.0.0.1",
                        "program": "${workspaceRoot}/src/server",
                        "env": {
                            "GOPATH":"D:/Develop/vscodegolang"
                        },
                        "args": [],
                        "showLog": true
                    }
                ]
            }

            "program"中的"${fileDirname}"是以當(dāng)前選中文件作為啟動(dòng)點(diǎn)

            更建議使用"program"的"${workspaceRoot}", 以包名作為啟動(dòng)點(diǎn)的方式進(jìn)行配置

            參考鏈接

            https://code.visualstudio.com/Docs/editor/debugging

            posted @ 2017-04-20 12:52 戰(zhàn)魂小筑 閱讀(8281) | 評論 (0)編輯 收藏

            2017年4月8日 #

            相位技術(shù)

            相位技術(shù)大規(guī)模出現(xiàn)在魔獸世界WLK版本, 現(xiàn)在應(yīng)用已經(jīng)廣泛應(yīng)用在各種MMORPG游戲中. 下面對相位技術(shù)的做法進(jìn)行簡單歸納匯總

            表現(xiàn)分類

            副本相位

            早期副本的出現(xiàn), 避免搶怪問題. 所以, 副本其實(shí)本身就是一種相位技術(shù). 只不過實(shí)現(xiàn)時(shí), 我們一般會(huì)將小隊(duì)和怪物直接預(yù)分配在獨(dú)立的一個(gè)副本實(shí)例中(所以副本原文也是實(shí)例的意思)

            分線相位

            相位技術(shù)還沒有正式命名時(shí), 同一個(gè)場景, 玩家進(jìn)到不同的分線看到的玩家不一樣, 也是屬于相位的一種. 當(dāng)然, 如果是組隊(duì)玩家, 服務(wù)器默認(rèn)會(huì)分配所有隊(duì)伍玩家在同一個(gè)線(位面)

            真相位

            副本相位和分線相位其實(shí)都是靜態(tài)相位, 一旦進(jìn)入, 中途不會(huì)有切換或者混合查看的過程. 真相位可以在一個(gè)場景中,動(dòng)態(tài)切換相位, 相位內(nèi)和相位外所以不同

            我們常見的真相位表現(xiàn)為: 相位中的角色+玩家+隊(duì)伍成員

            在護(hù)送任務(wù)時(shí), 還會(huì)在上面所見角色中疊加世界中的所有角色, 也就是說, 你和隊(duì)伍成員可以看到的角色, 其他人看不到, 其他人也看不到你和你的隊(duì)伍成員


            為了清晰的簡單的描述, 我為相位創(chuàng)建如下概念與名詞

            相位客體

            表現(xiàn)為除玩家外的角色(怪物,交互物體與相位可見場景)

            私有客體

            這是最常見的一種相位內(nèi)角色

            • 持有變量

              取PhasingID時(shí),為PhasingTargetID

            • 生成規(guī)則

              當(dāng)玩家開啟相位后, 在玩家相位內(nèi)生成的角色為私有客體.

              此時(shí), 將 PhasingTargetID設(shè)置為相位生成者的實(shí)例ID

            • 刪除規(guī)則

              如果玩家退出相位, 私有客體會(huì)存在一段時(shí)間或按照需求刪除

            公共客體

            一般指提前放置在場景中, 世界內(nèi)不可見, 但是能被同相位玩家可見,且同相位玩家都可以互相可見 比如: 只要接了同一個(gè)任務(wù)的玩家, 都可以看到的NPC

            • 持有變量

              取PhasingID時(shí),為PublicPhasingID

            • 生成規(guī)則

              通過場景編輯器, 放置角色時(shí), 設(shè)置其可被觀察到的任務(wù)ID

              角色被加載后, 將任務(wù)ID設(shè)置到StaticPhasingID

            • 刪除規(guī)則

              場景刪除, 角色刪除

            相位主體

            包含玩家與同隊(duì)伍玩家

            • 開啟相位后, 可見私有客體+公有客體

            • 隊(duì)長視為相位主體, 單人時(shí), 自己為隊(duì)長

            • 隊(duì)伍其他成員共享隊(duì)長的私有相位客體

            • 隊(duì)伍其他成員根據(jù)自己的PublicPhasingID匹配目標(biāo)對象的PublicPhasingID時(shí)可互相可見

            • 持有變量

            相位開啟時(shí), 取PhasingID時(shí), 為角色實(shí)例ID

            相位關(guān)閉時(shí), 取PhasingID時(shí), 為0

            PublicPhasingID

            可見規(guī)則

            當(dāng)兩個(gè)角色的PhasingID相等時(shí), 主體與私有客體互相可見

            當(dāng)兩個(gè)角色的PublicPhasingID相等時(shí), 主體與公有客體互相可見

            可以通過開關(guān)設(shè)置, 是否在可見的相位客體基礎(chǔ)上, 疊加世界角色(護(hù)送任務(wù))

            約束

            • 玩家同時(shí)只能看見1個(gè)相位
            posted @ 2017-04-08 14:41 戰(zhàn)魂小筑 閱讀(19738) | 評論 (0)編輯 收藏

            2016年12月1日 #

            Golang中沒有設(shè)計(jì)構(gòu)造函數(shù). 取而代之的, 設(shè)計(jì)Golang的大師希望你用普通函數(shù)去實(shí)現(xiàn)構(gòu)造的任務(wù). 
            一直只是覺得這只是體現(xiàn)Golang這門新語言的精簡設(shè)計(jì)之道, 直到自己實(shí)現(xiàn)編譯器后才發(fā)現(xiàn)構(gòu)造函數(shù)的設(shè)計(jì)本身是值得商榷的

            我們先看下構(gòu)造函數(shù)的規(guī)則

            構(gòu)造函數(shù)調(diào)用規(guī)則

            構(gòu)造參數(shù)量: 0表示沒有構(gòu)造函數(shù), 1表示有構(gòu)造函數(shù)0個(gè)參數(shù)

            本類構(gòu)造父類構(gòu)造處理方法
            00不處理
            10調(diào)本類ctor
            01調(diào)父類ctor
            11調(diào)本類ctor, 本類ctor調(diào)父類ctor
            21調(diào)本類ctor, 本類ctor調(diào)父類ctor
            12報(bào)錯(cuò), 手動(dòng)調(diào)父類ctor
            22報(bào)錯(cuò), 手動(dòng)調(diào)父類ctor

            普通函數(shù)重載規(guī)則

            實(shí)際只用考慮最典型的一種行為: 實(shí)例化子類, 轉(zhuǎn)為父類調(diào)用方法, 這個(gè)時(shí)候

            如果方法是override, 調(diào)用的是子類

            如果方法是virutal或者不指明, 調(diào)用的是父類

            整個(gè)重載過程, 子類絕對不會(huì)隱式調(diào)用父類的行為

            需要構(gòu)造函數(shù)么?

            構(gòu)造函數(shù)的優(yōu)點(diǎn)

            • 本身屬于一種特殊的成員函數(shù)
            • 編譯器幫你自動(dòng)傳導(dǎo)調(diào)用父級

            構(gòu)造函數(shù)的缺點(diǎn)

            • 隱式的調(diào)用規(guī)則
            • 雖然屬于成員函數(shù), 但是與其他成員函數(shù)調(diào)用規(guī)則完全不同, 需要特殊記憶
            • 帶參數(shù)的構(gòu)造函數(shù), 在父類參數(shù)多于子類時(shí), 需要引用復(fù)雜語法來實(shí)現(xiàn)父級構(gòu)造調(diào)用

            其實(shí)我們對初始化函數(shù)的需求只有1條: 自定義

            所以, 可以理解Golang不加入構(gòu)造函數(shù)的設(shè)計(jì)是正確的 
            即: 簡單, 清晰, 有規(guī)律

            posted @ 2016-12-01 10:45 戰(zhàn)魂小筑 閱讀(3341) | 評論 (0)編輯 收藏

            2016年11月2日 #

            append, map, len不是關(guān)鍵字

            他們其實(shí)還是類庫功能, 都在buildin包里的, 系統(tǒng)默認(rèn)給你做了個(gè)

            1. import(
            2. . "buildin"
            3. )

            將buildin的包內(nèi)容都映射到全局而已, 其實(shí)你也可以用自己的包這么做

            打印的另一種寫法

            想跟腳本一樣調(diào)試打印數(shù)據(jù)么?

            1. println("hello world")

            無需包含任何包, 因?yàn)樗赽uildin包里

            iota不是黑科技

            這是在buildin包里的定義

            1. // iota is a predeclared identifier representing the untyped integer ordinal
            2. // number of the current const specification in a (usually parenthesized)
            3. // const declaration. It is zero-indexed.
            4. const iota = 0 // Untyped int.

            其實(shí)go是有泛型概念的

            想想map和數(shù)組的定義 
            只是泛型沒有開放給用戶用而已(只許XX放火,不許XX點(diǎn)燈)

            map是支持多個(gè)key的, 而且很方便

            還在為多個(gè)key轉(zhuǎn)id的復(fù)雜算法而頭疼么?

            1. type myKey struct{
            2. number int
            3. str string
            4. }
            5. func main(){
            6. t := map[ myKey] int {
            7. myKey{ 2, "world"}: 1,
            8. }
            9. fmt.Println(t[myKey{2, "world"}])
            10. }
            11. 輸出: 1

            枚舉是可以轉(zhuǎn)成string的

            默認(rèn)定義一個(gè)枚舉

            1. type MyConst int
            2. const (
            3. MyConst_A MyConst = iota
            4. MyConst_B MyConst = iota
            5. )
            6. func main(){
            7. fmt.Println(MyConst_A)
            8. }

            輸出: 0 
            如果我們想自動(dòng)化輸出MyConst_A字符串時(shí) 
            就需要使用golang的一個(gè)工具鏈:golang.org/x/tools/cmd/stringer 
            將其下載, 編譯成可執(zhí)行工具后, 對代碼進(jìn)行生成 
            生成的代碼會(huì)多出一個(gè)xx_string.go 
            里面就是枚舉的String()string 函數(shù)

            臨時(shí)轉(zhuǎn)換一個(gè)接口并調(diào)用的方法

            1. type love struct{
            2. }
            3. func (self*love)foo(){
            4. fmt.Println("love")
            5. }
            6. func main(){
            7. var chaos interface{} = new(love)
            8. chaos.(interface{
            9. foo()
            10. }).foo()
            11. }

            Golang的receiver實(shí)際上就是this的變種實(shí)現(xiàn)

            1. func( self*MyStruct) foo( p int ){
            2. }

            寫不慣receiver的寫法? 如果這樣改下呢?

            1. func foo( self *MyStruct, p int ){
            2. }

            所以為什么說Golang還是一個(gè)C語言嘛

            關(guān)于內(nèi)存分配…

            • new 傳入Type類型, 返回*Type類型
            • make 可以在分配數(shù)組時(shí)設(shè)置預(yù)分配大小, new不可以
            • make 能分配數(shù)組,map, 但不能分配結(jié)構(gòu)體和原始類型
            • new 能分配數(shù)組, map, 結(jié)構(gòu)體和原始類型等的所有類型
            • &Type等效于new
            • 切片不需要分配內(nèi)存(make,new), 直接聲明就可以了…

            Golang的反射無法通過一個(gè)類型名, 創(chuàng)建其實(shí)例

            C#有Assembly概念, 可以在一個(gè)Assembly里搜索, 創(chuàng)建實(shí)例

            Golang是靜態(tài)類型語言, 如果需要, 只能注冊你需要?jiǎng)?chuàng)建的結(jié)構(gòu)體, 然后將注冊好的map用于創(chuàng)建

            Golang可以替換Python來進(jìn)行復(fù)雜的工具流程處理

            如果你需要跨平臺(tái)的工具流程處理, 對Python不熟悉, 可以使用

            1. go run yourcode.go 參數(shù)1 參數(shù)2

            方式來進(jìn)行工具處理 
            覺得慢, 可以編譯成可執(zhí)行文件

            這樣做的好處: 如果有些類庫本身就是go寫的, Python想使用是很麻煩的, 而Golang來寫則輕而易舉

            例子: 通過go的protobuf庫, 對一些文件進(jìn)行處理

            Golang可以自動(dòng)持有方法的接收者實(shí)例

            1. type myType struct{
            2. }
            3. func (self*myType) foo( p int){
            4. fmt.Println("hello", p )
            5. }
            6. func main(){
            7. var callback func( int )
            8. ins := new(myType)
            9. callback = ins.foo
            10. callback( 100 )
            11. }

            做過lua的C++代碼綁定的童鞋都清楚: lua只支持外部靜態(tài)或者全局函數(shù)調(diào)用 
            如果要進(jìn)行C++類成員函數(shù)調(diào)用時(shí), 要自己處理this和成員函數(shù) 
            這種技巧因?yàn)樵缙鹁幾g器的虛表不同平臺(tái)實(shí)現(xiàn)細(xì)節(jié)不統(tǒng)一需要專門處理 
            后面跨平臺(tái)虛表統(tǒng)一后, 類成員函數(shù)的調(diào)用寫法也是很惡心復(fù)雜的 
            但是Golang的小白式用法, 直接吊打C++, 甚至C#復(fù)雜的delegate

            LiteIDE篇: 多開秘籍

            • 找到 菜單->查看->選項(xiàng)->通用->存儲(chǔ)->存儲(chǔ)設(shè)置到本地ini文件

            • 關(guān)閉LiteIDE

            • 復(fù)制LiteIDE整個(gè)目錄, 命名文件夾為你的工程名

            • 每個(gè)工程所在的LiteIDE的配置將是獨(dú)立的, 不會(huì)互相干擾

            LiteIDE篇: 測試程序也是可以調(diào)試的

            別以為程序一定要是main開始的才可以調(diào)試

            Golang的測試程序雖然都是一個(gè)個(gè)Test開頭的函數(shù),但執(zhí)行g(shù)o test時(shí), 還是有main入口

            在LiteIDE中, 可以在 調(diào)試->開始調(diào)試測試程序里進(jìn)行測試程序調(diào)試

            LiteIDE篇: 在Windows上可以輸出linux可執(zhí)行文件

            go的工具鏈默認(rèn)支持交叉編譯 
            在LiteIDE中, 可以通過切換輸出平臺(tái), 輸出不同平臺(tái)的可執(zhí)行文件

            posted @ 2016-11-02 11:09 戰(zhàn)魂小筑 閱讀(21672) | 評論 (1)編輯 收藏

            2016年9月3日 #

            Google官方為Golang的調(diào)試?yán)幽J(rèn)使用了gdb

            然而, 使用gdb調(diào)試go程序會(huì)遇到goroutine的各類問題, 因?yàn)間db不懂go

            因此, 這里使用delve黑科技來進(jìn)行Golang的程序調(diào)試

            純命令行調(diào)試方法在網(wǎng)上很容易搜索到, 本文主要以LiteIDE來進(jìn)行程序調(diào)試

            關(guān)閉編譯器優(yōu)化

            正常go build/install出的go程序是完全優(yōu)化過的, 強(qiáng)行使用調(diào)試器掛接調(diào)試時(shí), 某些local變量/lamda表達(dá)式捕獲的變量會(huì)直接進(jìn)入寄存器, 無法使用調(diào)試器查看

            刪掉所有的pkg, 為build或install參數(shù)加入關(guān)閉編譯器優(yōu)化的參數(shù) -gcflags "-N -l"

            例如:

            1. go install -gcflags "-N -l" svc\gamesvc

            delve調(diào)試器安裝方法

            LiteIDE自帶了gdb, 但是沒有delve調(diào)試器, 需要自行安裝, 命令如下

            1. go get github.com/derekparker/delve/cmd/dlv

            delve調(diào)試器會(huì)被放到你的GOPATH/bin下

            LiteIDE中的delve調(diào)試器配置

            選擇調(diào)試器

            在LiteIDE菜單中選擇 調(diào)試->debugger/delve

            delve環(huán)境變量設(shè)置

            這個(gè)時(shí)候, LiteIDE依然找不到delve, 因?yàn)樗辉诃h(huán)境變量PATH中, 這里無需修改環(huán)境變量, 只需要LiteIDE的環(huán)境配置

            在LiteIDE菜單中選擇 查看->編輯當(dāng)前環(huán)境, 在彈出的文檔中修改

            1. PATH=c:\mingw32\bin;%GOROOT%\bin;%PATH%;c:\your\path\to\delve

            去掉PATH前的注釋#, 在%PATH%添加分號, 然后和你到delve調(diào)試器的路徑

            開始調(diào)試

            選擇你的工程, 點(diǎn)擊F5, 進(jìn)入調(diào)試模式

            調(diào)試器顯示變量值

            LiteIDE使用delve調(diào)試時(shí), 無法在 變量 監(jiān)視等窗口中正確捕捉delve調(diào)試返回?cái)?shù)據(jù)(因?yàn)楦袷教珡?fù)雜了…)

            沒關(guān)系, 我們使用命令行配合顯示即可

            LiteIDE控制臺(tái)或調(diào)試輸出窗口在delve調(diào)試時(shí), 實(shí)際上是一個(gè)標(biāo)準(zhǔn)命令行 
            命令如下

            • p 變量名可以查看變量值

            • locals查看局部變量

            • ls可查看當(dāng)前文件

            • stack查看棧

            • help可以查看各種幫助

            調(diào)試外部程序

            如果你的程序是外部程序, 或者使用go install安裝到GOPATH/bin目錄的程序, 那么使用delve調(diào)試器啟動(dòng)程序時(shí), 可能會(huì)碰到啟動(dòng)路徑錯(cuò)誤的問題

            使用LiteIDE菜單 調(diào)試->調(diào)試其他應(yīng)用程序… 填入你要調(diào)試程序的路徑以及工作目錄, 可以解決這個(gè)問題

            posted @ 2016-09-03 18:12 戰(zhàn)魂小筑 閱讀(5571) | 評論 (0)編輯 收藏

            僅列出標(biāo)題  下一頁
            久久精品国产半推半就| 久久久婷婷五月亚洲97号色| 国产精品伊人久久伊人电影| 久久久久成人精品无码| 99久久精品免费看国产一区二区三区 | 久久久婷婷五月亚洲97号色 | 亚洲日本va午夜中文字幕久久| 久久精品无码一区二区app| 久久大香萑太香蕉av| 久久青草国产手机看片福利盒子| 久久99国产一区二区三区| 色综合久久久久综合99| .精品久久久麻豆国产精品| 日韩久久久久中文字幕人妻| 精品熟女少妇av免费久久| 久久亚洲国产成人影院网站| 精品久久久久久亚洲精品| 久久亚洲视频| 中文字幕久久欲求不满| 亚洲AV无码久久精品狠狠爱浪潮| 99热热久久这里只有精品68| 久久国产色AV免费观看| 久久精品免费全国观看国产| 久久e热在这里只有国产中文精品99| 日韩精品久久久肉伦网站| 亚洲v国产v天堂a无码久久| 国产成人香蕉久久久久| 国产99久久精品一区二区| 久久亚洲AV成人无码国产| 狠狠色噜噜色狠狠狠综合久久| 青青草国产97免久久费观看| 久久青青草原国产精品免费| 99久久这里只有精品| 久久久久AV综合网成人| 欧美大香线蕉线伊人久久| 久久久久久综合网天天| 99久久精品国产一区二区| 久久综合久久美利坚合众国| 模特私拍国产精品久久| 亚洲精品午夜国产va久久| 人妻丰满?V无码久久不卡|