2011-04-20 38 views
4

目前我的遊戲服務器很小(一個區域和〜50 AI),並且每次發送狀態更新數據包(UDP)時,都會向每個客戶端發送一個完整狀態。這會創建大約1100字節的數據包大小。幾乎所有的發送是所有實體的以下信息:提供實時遊戲狀態更新的服務器

int uid 
int avatarImage 
float xPos 
float yPos 
int direction 
int attackState 
24 bytes 

編輯:更高效的結構

int uid 
byte avatarImage 
float xPos 
float yPos 
byte direction & attackState 
14 bytes 

但我將需要發送最終的詳細信息的實體。比如我加入到這個:

float targetXPos 
float targetYPos 
float speed 

由於需要更多的數據爲每個實體發送,我很快就要到來,很可能已經通過數據包的最大尺寸。所以我試圖想到一些可能的方法來解決我的問題:

1)只是建立狀態更新包,直到我用完了房間,然後剩下的其餘部分。非常糟糕的客戶視圖。不是一個真正的選擇。

2)只將N個最近實體的數據發送給客戶端。這要求每個狀態更新計算出每個客戶端最近的N.這可能非常耗時。

3)一些如何設計數據包,以便我可以發送多個相同的更新。目前,客戶端假定數據包的結構如下:

int currentMessageIndex 
int numberOfPCs 
N * PC Entity data 
int numberOfNPCs 
N * NPS Entity data 

然後客戶端接受這個新數據並完全覆蓋其狀態副本。由於數據包是完全獨立的,即使客戶端錯過了一個數據包,也是可以的。我不知道我將如何實現同一更新的多個數據包的想法,因爲如果我錯過其中的一個,那麼呢?我無法用更新,部分狀態覆蓋完整,過時的狀態。

4)只發送改變的實際變量。例如,對於每個實體,我添加一個int,這是每個字段的位掩碼。諸如速度,目標,方向和avatarImage等事物不需要在每次更新時發送。我仍然回到如果客戶端錯過實際需要更新其中一個值的數據包時會發生什麼情況的問題。我不知道這將是多麼重要。這還需要在客戶端和服務器端創建/讀取數據包時進行更多的計算,但不要太多。

有更好的想法嗎?

+0

我喜歡4號的想法。但是,如果任何信息相互連接(如速度和方向)。確保它們一起發送。如果在這兩種情況下客戶端都會丟失數據包......它仍然會錯過更新......數據是一次全部發送還是僅在發生更改時發送。 – 2011-04-20 06:44:33

+0

壓縮呢?或某種自定義編碼?如果事先知道某些值的範圍,則可以在每個字段上丟失一些比特... – Dan 2011-04-20 06:58:28

+1

我也喜歡數字4。除了發送* update *包之外,您還可以根據客戶端的要求發送* whole_status *包。當你在* update *包中包含序列號時,客戶端知道它應該何時請求* whole_status *。 – 2011-04-20 06:59:34

回答

3

我會用數字4和數字2去。 正如你已經意識到的那樣,通常只發送更新而不是完整的遊戲狀態。但要確保你總是發送絕對值而不是增量,這樣在丟棄數據包時不會丟失任何信息。您可以在客戶端使用航位推算法,在糟糕的網絡條件下儘可能平滑地製作動畫。 你必須爲此仔細設計,以便丟失數據包並不重要。

至於2號,如果你爲它設計的話,它不必費時。例如,您可以將您的遊戲區域劃分爲一個正方形網格,其中每個實體總是在一個特定的正方形中,讓遊戲世界跟蹤這一點。在這種情況下,計算9個surronding網格中的實體是O(1)操作。

+0

這不是一個網格座標系,你可以通過float xPos,yPos看到。沒有網格,我允許個人電腦重疊,所以你可以有50人正確的對方。 – 2011-04-20 07:23:53

0

一個問題,我跑進發送增量更新(基本上是你的第4個選項),是數據能進能出的順序或只是老了,這取決於你的服務器如何併發是,更新在服務器上的多個客戶端的主要競爭條件同一時間。

我的解決方案是更新通知發送給所有的客戶,有位掩碼設置爲位已更新的每個項目。

然後在客戶端請求基於掩碼的具體數據的當前值,這也允許客戶端只請求數據是感興趣的

這樣做的好處是避免了競爭條件和客戶端始終獲得最新的價值。

的缺點是它需要一個往返,獲得真正的價值。

更新來演示我試圖做的點。

假定4個客戶機A,B,C,d。 A和B同時向服務器Xa和Xb上的可變狀態X發送更新。由於B比A晚得多,服務器上X的最終狀態是X = Xb。

服務器發送更新後的狀態給所有的客戶端,所以C和D得到X的更新狀態,因爲交付順序是不確定的C得到Xa然後Xb和D得到Xb然後Xa,所以在這一點上,客戶端C和D對X的狀態有不同的想法,一個反映了服務器有另一個沒有的東西,它已經被棄用(或舊的)數據。

另一方面,如果服務器發出一個X已經更改爲所有客戶端的通知,C和D將獲得兩個X的狀態更改通知。它們都請求X的當前狀態,並且它們兩者都以Xb的服務器上的最終狀態X結束。

由於狀態通知的順序是無關緊要的,因爲在它沒有數據,以及客戶端發出的每個他們都結了一致的數據通知更新狀態的請求,

我希望這是我想說的更清楚些。

是它會增加延遲,但設計者必須決定什麼更重要的是,延遲或者讓所有客戶反映可變數據的相同狀態。這將取決於數據和遊戲。

+0

客戶端數據始終是舊的,因此強制客戶端請求它並不能解決此問題。唯一真正的問題是多大。如果服務器正確地管理世界狀態,那麼它應該能夠向任何感興趣的客戶發送更新而沒有問題。 – Kylotan 2011-04-20 11:41:11

+0

舊的含義不推薦使用。如果多個客戶端同時更新相同的數據,則無法保證一個客戶端不會收到過期的數據。併發意味着消息以非確定性順序接收。我的方法意味着你總是得到服務器知道的最新信息,並且永遠不會得到錯誤的數據,儘管它可能需要緊接着執行兩個請求。想想看,這很複雜。 – 2011-04-20 18:15:43

+1

在實時更新的情況下,您應該在發生更改時將更改後的狀態發送給感興趣的客戶。這是網絡上經典的觀察者模式。然後,無需往返即可獲得最新的價值,因爲您已擁有它或正在向您前進。無論採用哪種方式,您都必須假設您可能擁有舊數據,因爲信息需要時間才能從服務器傳輸到客戶端,並且自上次通知以來數據可能會再次發生更改。額外的往返行程只意味着您可以得到舊的數據。 – Kylotan 2011-04-20 18:29:32

1

這種類型的系統是使用Dead Reckoningpredictive contract算法通常解決。您不能依賴所有客戶端同時獲取更新,因此您需要根據先前已知的值預測位置,然後根據服務器生成的結果驗證這些預測。

+0

我已經知道了,但問題仍然是如何獲取所有數據到客戶端,以便它可以使用它來執行Dead Rechoning。它不能以某種速度和方向來完成,或者在這種情況下它正在尋找一個目標。 – 2011-04-20 20:18:27

+0

您無法保證每個客戶都會同時更新;從而進行推測。你的服務器應該是實體定位的權威來源;但每個客戶都可以根據您已經提供的數據(位置,方向,速度和目標)預測它和所有其他實體的去向。每x毫秒,你的服務器應該發送最新的位置變化量,你的客戶將用它來糾正他們的預測。這個數據包可以簡單地爲{uid,deltax,deltay}。您會看到單獨的數據包以瞭解速度,方向和目標更改。 – 2011-04-20 21:26:27

+0

你正在浪費原始數據包結構中的位。您可能沒有INT_MAX頭像圖片或攻擊狀態。您可以通過使用位掩碼而不是整數來減少這些內容。你也許可以對你的使用和方向也做同樣的事情。 – 2011-04-20 21:27:46