[程式] [UE4]網路同步之Character謀殺事件

作者: dorgonman (dorgonman)   2018-06-28 22:09:07
最近用UE4遇到了一個非常奇怪的現象,
困擾了好久,於是我決定用力追查下去…
網誌版:
http://dorgon.horizon-studio.net/zh/archives/1561
===============全文開始====================
事情是這樣的。
本來我是想要測一下network做replication變數相關的功能,因此建了一個空白的level(全黑色那張),並做了一個Character後加了幾個變數與寫了幾個function來進行實驗。在按下play之後……嗯,一開始變數成功的從server傳過去給client了(開了二個player,一個listen server,一個client)。很好!就在我滿足於事情正如想像中進展的時候,突然間,變數卻完全送不過去給Client了!
到底發生了什麼事?就我的理解上,我用的是COND_None,只要server有任何變動的話,變數應該會送給client阿?難不然變數是unreliable的?不對阿,replication變數肯定都是reliable,頂多就只有因為頻寬的問題而延遲送達而已。經過我的反覆實驗,最後發現了一件驚人的事實:
我listen server上的character居然被砍掉了!
在得知這項資訊後,我決定馬上到AMyCharacter::EndPlay這個function中下斷點──我想想看看到底是那個混蛋砍了我的character。可惜的是,我依然沒有抓到兇手。──AMyCharacter::EndPlay是被呼叫了沒錯,可是EndPlayReason只寫了一行Destroyed。這資訊完全沒有幫助,因此我決定循著call stack往上層追,最後看到了這段程式碼:
https://gist.github.com/dorgonman/00c3c173f3a70fc040d88d00915e3323
WTF!?網路收到了斷線通知之後,就馬上把我的角色砍了!?為什麼listen server上所擁有的Character會被斷線?我並沒有加入任何砍Actor的邏輯阿?難道是因為自動觸發GC的緣故?也不對阿,Actor的生命週期應該是跟著world才是,要砍Actor的話只能手動呼叫Destroy(K2_DestroyActor)才行。我完全被眼前的事實驚呆了,這跟我過去對於UE4相關的知識完全不相符。
「這樣不行,一定要找出原因。」──雖然感受到了挫折,但我才不會因為這點小事就被擊敗。為了找出被斷線的原因,我決定利用手邊僅剩唯一的蛛絲馬跡「Bunch.bClose」來找出殺死Character的兇手是誰。事出必有因,既然Bunch.bClose這個flag是true,那必然是有那段邏輯使然。在搜遍了整個引擎之後,總算發現下面這段非常可疑的程式碼:
温ttps://gist.github.com/dorgonman/5f021b9eac330493d3f012bfda9a8d99
當Channel->Close();被呼叫之後,就會送出一個CloseBunch出來並把我的Character殺死。而為什麼會進到這段邏輯的根本原因,在於bIsRecentlyRelevant是false,意即,listen server認為這個Character已經跟自己沒有任何瓜葛了,所以就殺了他。
到這裡,似乎就已經破案了,原來人是Server自己殺掉的阿……
但李組長眉頭一皺,事情似乎並不是這麼樣的單純。為什麼listen server會無緣無殺的去殺掉自己的親生兒子?難不成二兒子就那麼該死嗎(PlayerController2)?大兒子(PlayerController1,listen server)不是還活蹦亂跳的好好的在那邊嗎?為什麼就只有二兒子!?而且是非常固定,每次在世界被建構起來約10秒鐘之後,這件兇殺案就會必然的發生。不管是我模擬了幾十次還是幾百次,都無法逃離二兒子死亡的結局。
這世界上肯定是哪裡有Bug了!──我的心底對著Epic大神這麼叫囂著。
這麼顯而易見的Bug,不可能只有我才會遇到。於是我轉而求向古歌大神的協助……然而,卻還是一無所獲。沒辦法,只好靠著新發現的事實,繼續的往上追蹤。目前我們知道,當Listen Server決定要跟二兒子斷絕關係的時候,bIsRecentlyRelevant就會被設成false,那麼我們只要找到設置bIsRecentlyRelevant這個flag的源頭,就必然有辦法找出這件兇殺案背後發生的原因。
殺人兇手,是身為父親的Listen Server。──這點已經不容質疑。
但事實的背後往往有著更令人訝異的真實。只要努力不懈的話,真理永遠就只會有一個。是的,最後我終於到達了,那扇真理之門的面前:
https://gist.github.com/dorgonman/baba67405e381f52f35f7112c7b610a7
看到這段程式碼之後,我馬上理解了整個案情的來龍去脈。為了證明事實真的如同我所想像的,我馬上再次開啟了最後一次世界的模擬:
「failllllllllllllllllllllllllllllllllllllling──────!」我彷彿聽到了慘叫聲。
二兒子Z軸的數字正以著不可思議的速度往下遞減──在重力加速度的加成下,二兒子快速的離大兒子遠去,然後在到達一定的臨界值(NetCullDistanceSquared)之後,就這麼消失在世界的盡頭。
什麼嘛,原來是摔死的。
至此,一切的謎題都已經解開了。
想要避免這件慘絕人寰的兇殺案,大致上有以下幾個解決方案:
將CharacterMovement中的Default Land Movement Mode 設成Flying:這樣你的角色就不會掉下去了。
將Character中的Always Relevant這個選項打勾:但是你的Character還是會無限的往下進行自由落體的動作,只是,這個flag是有其必要性,它在多人連線遊戲下可以減少不少網路頻寬,在調整這個設置前,最好對這個flag有正確的認識。
下面放個Cube:這樣Character就有地方可以站。
作者: coolrobin (泳圈)   2018-06-29 00:02:00
推推
作者: dklassic (DK)   2018-06-29 12:18:00
這也寫得太有趣了吧 Xd
作者: ConSeR (草履重根)   2018-07-05 18:28:00
摔死XD

Links booklink

Contact Us: admin [ a t ] ucptt.com