貿易時代的總結(二)--邏輯服務器和地圖服務器

邏輯服務器的設計:
    邏輯服務器的設計思路其實很簡單,我們把遊戲裏的對象分爲2大類,城市和人。所有的遊戲邏輯操作都是圍繞這這兩個對象進行的。其中城市對象又還可以細分爲多個子對象,這些對象在結構上是屬於城市的,它們分別是碼頭、船廠、交易所、銀行、酒館。
    在遊戲進行的時候,玩家要進行某些操作的時候(購買貨物)會向服務器端發送遊戲消息,這時候,服務器在收到消息後,會通過消息玩家的ID知道玩家當前在那個城市裏,然後再由這個城市對象去處理這條遊戲消息。在城市對象收到遊戲消息後,首先是對一級分類進行判斷,這個級別的分類標準是邏輯上的分類,這點在前面已經說過了。這時候如果消息的邏輯分類是交易所,城市對象,會再調用城市對象裏面的交易所對象去執行該條遊戲消息,這時候,城市對象就要根據消息的二級分類來判斷玩家到底是要做什麼事情了。這時候,如果消息是BuyReq 者說明玩家是打算購買一類貨物,交易所對象,再從消息裏附帶的結構體變量讀出要購買貨物的編號和數量。這時候交易所首先要判斷,這類貨物在本交易所裏時候存在,並且能夠賣,如果可以,再調用用戶對象的購買貨物函數,由用戶對象進行下一步的處理(看看時候有足夠的錢,玩家的船是否還能裝得下這個貨物等等..)如果可以,則由用戶類返回成功,否則,返回錯誤的信息。在交易所收到用戶類返回的信息後,再根據返回的結果,生成相應的遊戲消息發還給用戶。
    整個過程很簡單,所以,在項目開始的時候,我就先那這個系統開刀,在分配項目任務的時候,我也就有限考慮自己做簡單的東西,然後把複雜的地圖服務器仍給了另外一位同學^_^。不過雖然這個模塊簡單,不過涉及到的東西很多,難度雖然不大,但是很煩瑣,一個小小的問題就可能造成今後遊戲存在的嚴重bug,所以在進行沒有邏輯事件的處理都是時候,我都是很小心的在做,剛開始的時候,還寫過一定的測試用例去測試系統,不過現在看來,當時寫的測試用例存在有一定的問題:
1。是在完成代碼後才寫測試,XP方法要求測試用例要優先與代碼的編寫。
2。只寫了測試過程,沒有編寫自動化的結果判斷。有測試,就必須要有預期的測試結果,當時寫的測試用例大多隻是看看那個函數能夠正確運行而已,所有結果都是直接輸出到屏幕上,讓我自己來判斷,在測試用例寫過一段時間後,要測試的部分太多,有些看不過來了,所以寫了測試輸入,一定要想好輸出結果,並且要讓測試代碼自己把函數返回結果與想好的輸出對結果進行判斷,從而減少人工判斷的錯誤性。
3。測試用例過少,因爲時間的關係,只是在前面的階段寫過一定的測試用例,到後來進度緊張後,就沒空去進行測試了。歸結起來,應該是沒有一個良好的測試框架與測試機制,有好的框架,可以減少測試用例的編寫時間,從而有更多的時間去編寫代碼。同時因爲項目不是什麼正規的項目,所以對測試的要求也不是太嚴格。
    除了測試存在不足外,程序的整個結構上也存在着嚴重的不足:
1。當初因爲考慮得簡單,每個玩家的遊戲消息都會有一個與它相對應的遊戲消息,所以,我在程序裏大膽的採用了函數返回遊戲消息指針的方式,在系統完成遊戲消息處理後,產生一條遊戲消息,並且把改遊戲消息以返回消息內存首地址指針的方式返回給調用處理的函數。這樣一層層的返回,一直返回到socket的OnRead()函數,然後再在這個函數裏send數據給客戶端。這樣的設計在剛開始的時候,並沒有覺得什麼不託,但是實際到後期才發覺,這樣的設計有一個很大的不方便,有些從客戶端來的遊戲消息,它的返回消息可能不只一個,所以在一個函數的返回值裏就不太好處理了。這個問題我開始的時候沒有太注意,因爲很多消息都只是有一條返回消息而已,哪知到後來系統的擴展,很多函數處理都要返回多條數據,所以,在後來的不得不在邏輯處理裏面增加消息發送函數。不過這樣給我的感覺就不是很舒服了,邏輯處理裏面就不應該存在其它的東西,現在增加的消息的發送,就不是很好了。不過一直到現在,我都沒有找到合適的結局方法,所以,如果在一開始的時候先想好消息該怎麼發送,就不會存在這樣的混亂了。
2。在前面的代碼裏,遺漏有創建遊戲消息的代碼(交易所),後來感覺不爽,因爲要創建的東西很多,而且很多都是重複的,所以就用一個模版來處理創建過程。再到了後來,覺得邏輯代碼裏存在很多遊戲邏輯消息的變量賦值過程,這樣的代碼也不是很好看,所以有進一步把這個東西獨立出來做個函數,這樣邏輯處理代碼就好看很多了。這個過程是一個不斷的發展過程,也都是看到不足後才進行處理,所以,如果當初就和上面的遊戲數據發送一起考慮好,就不會留下很多垃圾代碼(這些代碼我現在還沒有處理掉)。
 
地圖服務器的設計:
    對於地圖服務器的設計,在網上關於它的文章一直都比較多,說得最多的就是如何實現讓客戶端與服務器進行同步。所以,在項目開始的時候,這個部分能夠參考的文章很多,不過也因爲這個系統是交給一個完全沒有做過遊戲的同學來製作,所以很多時候,很多概念性的東西需要理解,在理解的時候會造成一定的偏差,所以系統在實現的時候不是很完善。不過也因爲進行開發的這位同學的實力不同一般,所以地圖服務器還是能夠運行的,只不過^_^
    當初設計的時候,考慮到遊戲裏存在着2種移動場景,一個是海上,一個是城市。城市的特點是有邊界,海上地圖的特點是超大無縫地圖,所以在設計的時候,我們有限考慮的是海上場景的地圖設計。城市地圖可以看做是海上地圖的一個特例,地圖的四周只有少量或者根本沒有相關的連接地圖。
    超大地圖的設計是採取對地圖進行分塊處理,把整個海洋世界分解爲多塊小地圖,每塊地圖的大小和一個遊戲屏幕(800×600)的大小差不多。服務器裏存放有所有地圖的信息,當一位玩家參數移動的時候,會把自己的移動請求信息發送到相關的9塊地圖上(自己所在的地圖,以及以自己爲中心鄰接的8張地圖),這樣每個客戶端都能接收到來自自己周圍9張地圖上其他玩家的移動信息,從而實現了系統的互動。
    概念上說起來是很簡單,但是到實踐的時候要考慮的地方就很多了,例如移動方向,阻擋,最麻煩的就是地圖切換了,需要通知相關地圖的玩家進入以及相關地圖上的玩家退出等遊戲事件,而且還存在則跨越地圖後的移動處理等問題。總之因爲我只是提供方案者,具體編碼不是我來做,所以我對裏面的具體細節瞭解得不是很詳細,只能大概的理一下他的思路。
    服務器還是基於面向對象的設計思路來做的,整個系統裏分了3個對象,玩家、地圖、地圖管理。玩家對象和我上面的概念一樣,是存儲玩家信息,並存在着一定的於地圖相關的處理函數。地圖對象存放的是一小張地圖的信息,和與當張地圖操作相關的操作。地圖管理可以看做是超大地圖類,它的作用就是管理無數個地圖對象,讓它們在邏輯上拼接成爲一張超大的地圖。
    同時除了這3個對象外,還存在着一個狀態機的過程。我們對這個狀態機的理解很簡單,就是每隔一定的週期計算一次玩家的新位置,讓他們看起來是在連續不斷的運動。這個東西不是很難理解,但是處理起來確看起來有些讓人困擾,首先是服務器端的在虛擬機執行時間中斷的時間間隔與客戶端的不同,設計的時候,服務器端是每隔0.5s執行一次,而客戶端如果也按照服務器端的設計出處理,明顯會讓玩家感覺地圖上的人物一卡一卡的在進行跳躍運動,所以客戶端在虛擬機的時間間隔勢必要比服務器短很多,我們的設計是0.1S執行一次。這樣的短的間隔就會造成客戶端的移動數據與服務器端的移動數據不一致。解決這個不同步的問題採用了多種方案,其中包括定期發送玩家的位置的同步信息。不過也因爲時間關係,這個同步方案沒有完全的實現,所以系統還是存在着不同步的現象。
    不過地圖服務器裏的註解還是比較詳細的,所以這裏要說明的不是很多,理解它架構就好理解了。理解架構後再看一下“地圖服務器工作日誌.doc”會對這個開發過程有一定的瞭解。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章