工具和網絡遊戲封包基礎

 截包工具:

  http://free.ys168.com/?shoooo

  WPRO.rar      MD5: 2E1E8E48FCC78972905E4F40081B608C

  IRIS.rar         MD5: 6B919165C60D83379C1F825245100545

  WPE 是通過注入dll的方式hook send, recv, WSASend, WSARecv等函數來截取封包, 如果進程有anti wpe , 有可能截不到數據包

  Iris 是sniffer工具, 對網卡進行截包, 需要適當設置IP,port過濾條件, 不可能漏包

  調試工具:

  flyOdbg 1.1

  http://download.winzheng.com/SoftView/SoftView_30342.htm

  fly修改的ollydbg漢化第二版, 很棒的調試工具, 你能在bbs.pediy.com (看雪論壇)找到很多教程

  分析工具:

  IDA 4.8

  http://www.crsky.com/soft/1604.html

  16進制編輯工具:

  UE 11.0

  http://www.ultraedit.com/index.php?name=Downloads&;d_op=getit&lid=1

  http://bbs.pediy.com/upload/2005/10/files/UltraEdit.v11.00.Keymaker.zip

  要想在修改遊戲中做到百戰百勝,是需要相當豐富的計算機知識的。有很多計算機高手就是從玩遊戲,修改遊戲中,逐步

  對計算機產生濃厚的興趣,逐步成長起來的。不要在羨慕別人能夠做到的,因爲別人能夠做的你也能夠!我相信你們看了

  本教程後,會對遊戲有一個全新的認識,呵呵,因爲我是個好老師!(別拿雞蛋砸我呀,救命啊!#¥%……*)   不過

  要想從修改遊戲中學到知識,增加自己的計算機水平,可不能只是靠修改遊戲呀! 要知道,修改遊戲只是一個驗證你對

  你所瞭解的某些計算機知識的理解程度的場所,只能給你一些發現問題、解決問題的機會,只能起到幫助你提高學習計算

  機的興趣的作用,而決不是學習計算機的捷徑。

  一:什麼叫外掛?

  現在的網絡遊戲多是基於Internet上客戶/服務器模式,服務端程序運行在遊戲服務器上,遊戲的設計者在其中創造

  一個龐大的遊戲空間,各地的玩家可以通過運行客戶端程序同時登錄到遊戲中。簡單地說,網絡遊戲實際上就是由遊戲開

  發商提供一個遊戲環境,而玩家們就是在這個環境中相對自由和開放地進行遊戲操作。那麼既然在網絡遊戲中有了服務器

  這個概念,我們以前傳統的修改遊戲方法就顯得無能爲力了。記得我們在單機版的遊戲中,隨心所欲地通過內存搜索來修

  改角色的各種屬性,這在網絡遊戲中就沒有任何用處了。因爲我們在網絡遊戲中所扮演角色的各種屬性及各種重要資料都

  存放在服務器上,在我們自己機器上(客戶端)只是顯示角色的狀態,所以通過修改客戶端內存裏有關角色的各種屬性是

  不切實際的。那麼是否我們就沒有辦法在網絡遊戲中達到我們修改的目的?回答是"否"。

  我們知道Internet客戶/服務器模式的通訊一般採用TCP/IP通信協議,數據交換是通過IP數據包的傳輸來實現的,一

  般來說我們客戶端向服務器發出某些請求,比如移動、戰鬥等指令都是通過封包的形式和服務器交換數據。那麼我們把本

  地發出消息稱爲SEND,意思就是發送數據,服務器收到我們SEND的消息後,會按照既定的程序把有關的信息反饋給客戶端

  ,比如,移動的座標,戰鬥的類型。那麼我們把客戶端收到服務器發來的有關消息稱爲RECV。知道了這個道理,接下來我

  們要做的工作就是分析客戶端和服務器之間往來的數據(也就是封包),這樣我們就可以提取到對我們有用的數據進行修

  改,然後模擬服務器發給客戶端,或者模擬客戶端發送給服務器,這樣就可以實現我們修改遊戲的目的了。

  目前除了修改遊戲封包來實現修改遊戲的目的,我們也可以修改客戶端的有關程序來達到我們的要求。我們知道目前

  各個服務器的運算能力是有限的,特別在遊戲中,遊戲服務器要計算遊戲中所有玩家的狀況幾乎是不可能的,所以有一些

  運算還是要依靠我們客戶端來完成,這樣又給了我們修改遊戲提供了一些便利。比如我們可以通過將客戶端程序脫殼來發

  現一些程序的判斷分支,通過跟蹤調試我們可以把一些對我們不利的判斷去掉,以此來滿足我們修改遊戲的需求。 在下

  幾個章節中,我們將給大家講述封包的概念,和修改跟蹤客戶端的有關知識。大家準備好了嗎?

  遊戲數據格式和存儲:

  在進行我們的工作之前,我們需要掌握一些關於計算機中儲存數據方式的知識和遊戲中儲存數據的特點。本章節是提

  供給菜鳥級的玩家看的,如果你是高手就可以跳過了,如果,你想成爲無堅不摧的劍客,那麼,這些東西就會花掉你一些

  時間;如果,你只想作個江湖的遊客的話,那麼這些東西,瞭解與否無關緊要。是作劍客,還是作遊客,你選擇吧!

  現在我們開始!首先,你要知道遊戲中儲存數據的幾種格式,這幾種格式是:字節(BYTE)、字(WORD)和雙字(DOUBLE

  WORD),或者說是8位、16位和32位儲存方式。字節也就是8位方式能儲存0~255的數字;字或說是16位儲存方式能儲存

  0~65535的數;雙字即32位方式能儲存0~4294967295的數。

  爲何要了解這些知識呢?在遊戲中各種參數的最大值是不同的,有些可能100左右就夠了,比如,金庸羣俠傳中的角

  色的等級、隨機遇敵個數等等。而有些卻需要大於255甚至大於65535,象金庸羣俠傳中角色的金錢值可達到數百萬。所以

  ,在遊戲中各種不同的數據的類型是不一樣的。在我們修改遊戲時需要尋找準備修改的數據的封包,在這種時候,正確判

  斷數據的類型是迅速找到正確地址的重要條件。

  在計算機中數據以字節爲基本的儲存單位,每個字節被賦予一個編號,以確定各自的位置。這個編號我們就稱爲地址

  。

  在需要用到字或雙字時,計算機用連續的兩個字節來組成一個字,連續的兩個字組成一個雙字。而一個字或雙字的地

  址就是它們的低位字節的地址。 現在我們常用的Windows 9x操作系統中,地址是用一個32位的二進制數表示的。而在平

  時我們用到內存地址時,總是用一個8位的16進制數來表示它。

  二進制和十六進制又是怎樣一回事呢?

  簡單說來,二進制數就是一種只有0和1兩個數碼,每滿2則進一位的計數進位法。同樣,16進制就是每滿十六就進一

  位的計數進位法。16進制有0--F十六個數字,它爲表示十到十五的數字採用了A、B、C、D、E、F六個數字,它們和十進制

  的對應關係是:A對應於10,B對應於11,C對應於12,D對應於13,E對應於14,F對應於15。而且,16進制數和二進制數間

  有一個簡單的對應關係,那就是;四位二進制數相當於一位16進制數。比如,一個四位的二進制數1111就相當於16進制的

  F,1010就相當於A。

  瞭解這些基礎知識對修改遊戲有着很大的幫助,下面我就要談到這個問題。由於在計算機中數據是以二進制的方式儲

  存的,同時16進制數和二進制間的轉換關係十分簡單,所以大部分的修改工具在顯示計算機中的數據時會顯示16進制的代

  碼,而且在你修改時也需要輸入16進制的數字。你清楚了吧?

  在遊戲中看到的數據可都是十進制的,在要尋找並修改參數的值時,可以使用Windows提供的計算器來進行十進制和

  16進制的換算,我們可以在開始菜單裏的程序組中的附件中找到它。

  現在要了解的知識也差不多了!不過,有個問題在遊戲修改中是需要注意的。在計算機中數據的儲存方式一般是低位

  數儲存在低位字節,高位數儲存在高位字節。比如,十進制數41715轉換爲16進制的數爲A2F3,但在計算機中這個數被存

  爲F3A2。

  看了以上內容大家對數據的存貯和數據的對應關係都瞭解了嗎? 好了,接下來我們要告訴大家在遊戲中,封包到底

  是怎麼一回事了,來!大家把袖口捲起來,讓我們來幹活吧!

  二:什麼是封包?

  怎麼截獲一個遊戲的封包?怎麼去檢查遊戲服務器的ip地址和端口號? Internet用戶使用的各種信息服務,其通訊

  的信息最終均可以歸結爲以IP包爲單位的信息傳送,IP包除了包括要傳送的數據信息外,還包含有信息要發送到的目的IP

  地址、信息發送的源IP地址、以及一些相關的控制信息。當一臺路由器收到一個IP數據包時,它將根據數據包中的目的IP

  地址項查找路由表,根據查找的結果將此IP數據包送往對應端口。下一臺IP路由器收到此數據包後繼續轉發,直至發到目

  的地。路由器之間可以通過路由協議來進行路由信息的交換,從而更新路由表。

  那麼我們所關心的內容只是IP包中的數據信息,我們可以使用許多監聽網絡的工具來截獲客戶端與服務器之間的交換

  數據,下面就向你介紹其中的一種工具:WPE。

  WPE使用方法:執行WPE會有下列幾項功能可選擇:

  SELECT GAME選擇目前在記憶體中您想攔截的程式,您只需雙擊該程式名稱即可。

  TRACE追蹤功能。用來追蹤擷取程式送收的封包。WPE必須先完成點選欲追蹤的程式名稱,纔可以使用此項目。 按下

  Play鍵開始擷取程式收送的封包。您可以隨時按下 | | 暫停追蹤,想繼續時請再按下 | | 。按下正方形可以停止擷取封

  包並且顯示所有已擷取封包內容。若您沒按下正方形停止鍵,追蹤的動作將依照OPTION裏的設定值自動停止。如果您沒有

  擷取到資料,試試將OPTION裏調整爲Winsock Version 2。WPE 及 Trainers 是設定在顯示至少16 bits 顏色下才可執行

  。

  FILTER過濾功能。用來分析所擷取到的封包,並且予以修改。

  SEND PACKET送出封包功能。能夠讓您送出假造的封包。

  TRAINER MAKER製作修改器。

  OPTIONS設定功能。讓您調整WPE的一些設定值。

  FILTER的詳細教學

  - 當FILTER在啓動狀態時 ,ON的按鈕會呈現紅色。- 當您啓動FILTER時,您隨時可以關閉這個視窗。FILTER將會保

  留在原來的狀態,直到您再按一次 on / off 鈕。- 只有FILTER啓用鈕在OFF的狀態下,纔可以勾選Filter前的方框來編

  輯修改。- 當您想編輯某個Filter,只要雙擊該Filter的名字即可。

  NORMAL MODE:

  範例:

  當您在 Street Fighter Online ﹝快打旋風線上版﹞遊戲中,您使用了兩次火球而且擊中了對方,這時您會擷取到

  以下的封包:SEND-> 0000 08 14 21 06 01 04 SEND-> 0000 02 09 87 00 67 FF A4 AA 11 22 00 00 00 00 SEND->

  0000 03 84 11 09 11 09 SEND-> 0000 0A 09 C1 10 00 00 FF 52 44 SEND-> 0000 0A 09 C1 10 00 00 66 52 44

  您的第一個火球讓對方減了16滴﹝16 = 10h﹞的生命值,而您觀察到第4跟第5個封包的位置4有10h的值出現,應該就

  是這裏了。

  您觀察10h前的0A 09 C1在兩個封包中都沒改變,可見得這3個數值是發出火球的關鍵。

  因此您將0A 09 C1 10填在搜尋列﹝SEARCH﹞,然後在修改列﹝MODIFY﹞的位置4填上FF。如此一來,當您再度發出火

  球時,FF會取代之前的10,也就是攻擊力爲255的火球了!

  ADVANCED MODE:

  範例: 當您在一個遊戲中,您不想要用真實姓名,您想用修改過的假名傳送給對方。在您使用TRACE後,您會發現有

  些封包裏面有您的名字出現。假設您的名字是Shadow,換算成16進位則是﹝53 68 61 64 6F 77﹞;而您打算用moon﹝6D

  6F 6F 6E 20 20﹞來取代他。1) SEND-> 0000 08 14 21 06 01 042) SEND-> 0000 01 06 99 53 68 61 64 6F 77 00 01

  05 3) SEND-> 0000 03 84 11 09 11 094) SEND-> 0000 0A 09 C1 10 00 53 68 61 64 6F 77 00 11 5) SEND-> 0000 0A

  09 C1 10 00 00 66 52 44

  但是您仔細看,您的名字在每個封包中並不是出現在相同的位置上

  - 在第2個封包裏,名字是出現在第4個位置上- 在第4個封包裏,名字是出現在第6個位置上

  在這種情況下,您就需要使用ADVANCED MODE- 您在搜尋列﹝SEARCH﹞填上:53 68 61 64 6F 77 ﹝請務必從位置1開

  始填﹞- 您想要從原來名字Shadow的第一個字母開始置換新名字,因此您要選擇從數值被發現的位置開始替代連續數值﹝

  from the position of the chain found﹞。- 現在,在修改列﹝MODIFY﹞000的位置填上:6D 6F 6F 6E 20 20 ﹝此爲

  相對應位置,也就是從原來搜尋欄的+001位置開始遞換﹞- 如果您想從封包的第一個位置就修改數值,請選擇﹝from the

  beginning of the packet﹞

  瞭解一點TCP/IP協議常識的人都知道,互聯網是將信息數據打包之後再傳送出去的。每個數據包分爲頭部信息和數據

  信息兩部分。頭部信息包括數據包的發送地址和到達地址等。數據信息包括我們在遊戲中相關操作的各項信息。那麼在做

  截獲封包的過程之前我們先要知道遊戲服務器的IP地址和端口號等各種信息,實際上最簡單的是看看我們遊戲目錄下,是

  否有一個SERVER.INI的配置文件,這個文件裏你可以查看到個遊戲服務器的IP地址,比如金庸羣俠傳就是如此,那麼除了

  這個我們還可以在DOS下使用NETSTAT這個命令,

  NETSTAT命令的功能是顯示網絡連接、路由表和網絡接口信息,可以讓用戶得知目前都有哪些網絡連接正在運作。或

  者你可以使用木馬客星等工具來查看網絡連接。工具是很多的,看你喜歡用哪一種了。

  NETSTAT命令的一般格式爲:NETSTAT [選項]

  命令中各選項的含義如下:-a 顯示所有socket,包括正在監聽的。-c 每隔1秒就重新顯示一遍,直到用戶中斷它。

  -i 顯示所有網絡接口的信息。-n 以網絡IP地址代替名稱,顯示出網絡連接情形。-r 顯示核心路由表,格式同"route -

  e"。-t 顯示TCP協議的連接情況。-u 顯示UDP協議的連接情況。-v 顯示正在進行的工作。

  三:怎麼來分析我們截獲的封包?

  首先我們將WPE截獲的封包保存爲文本文件,然後打開它,這時會看到如下的數據(這裏我們以金庸羣俠傳裏PK店小

  二客戶端發送的數據爲例來講解):

  第一個文件:SEND-> 0000 E6 56 0D 22 7E 6B E4 17 13 13 12 13 12 13 67 1BSEND-> 0010 17 12 DD 34 12 12

  12 12 17 12 0E 12 12 12 9BSEND-> 0000 E6 56 1E F1 29 06 17 12 3B 0E 17 1ASEND-> 0000 E6 56 1B C0 68 12 12

  12 5ASEND-> 0000 E6 56 02 C8 13 C9 7E 6B E4 17 10 35 27 13 12 12SEND-> 0000 E6 56 17 C9 12

  第二個文件:SEND-> 0000 83 33 68 47 1B 0E 81 72 76 76 77 76 77 76 02 7ESEND-> 0010 72 77 07 1C 77 77

  77 77 72 77 72 77 77 77 6DSEND-> 0000 83 33 7B 94 4C 63 72 77 5E 6B 72 F3SEND-> 0000 83 33 7E A5 21 77 77

  77 3FSEND-> 0000 83 33 67 AD 76 CF 1B 0E 81 72 75 50 42 76 77 77SEND-> 0000 83 33 72 AC 77

  我們發現兩次PK店小二的數據格式一樣,但是內容卻不相同,我們是PK的同一個NPC,爲什麼會不同呢? 原來金庸羣

  俠傳的封包是經過了加密運算纔在網路上傳輸的,那麼我們面臨的問題就是如何將密文解密成明文再分析了。

  因爲一般的數據包加密都是異或運算,所以這裏先講一下什麼是異或。 簡單的說,異或就是"相同爲0,不同爲1"(

  這是針對二進制按位來講的),舉個例子,0001和0010異或,我們按位對比,得到異或結果是0011,計算的方法是:0001

  的第4位爲0,0010的第4位爲0,它們相同,則異或結果的第4位按照"相同爲0,不同爲1"的原則得到0,0001的第3位爲0,

  0010的第3位爲0,則異或結果的第3位得到0,0001的第2位爲0,0010的第2位爲1,則異或結果的第2位得到1,0001的第1

  位爲1,0010的第1位爲0,則異或結果的第1位得到1,組合起來就是0011。異或運算今後會遇到很多,大家可以先熟悉熟

  悉,熟練了對分析很有幫助的。

  下面我們繼續看看上面的兩個文件,按照常理,數據包的數據不會全部都有值的,遊戲開發時會預留一些字節空間來

  便於日後的擴充,也就是說數據包裏會存在一些"00"的字節,觀察上面的文件,我們會發現文件一里很多"12",文件二里

  很多"77",那麼這是不是代表我們說的"00"呢?推理到這裏,我們就開始行動吧!

  我們把文件一與"12"異或,文件二與"77"異或,當然用手算很費事,我們使用"M2M 1.0 加密封包分析工具"來計算就

  方便多了。得到下面的結果:

  第一個文件:1 SEND-> 0000 F4 44 1F 30 6C 79 F6 05 01 01 00 01 00 01 75 09SEND-> 0010 05 00 CF 26 00 00

  00 00 05 00 1C 00 00 00 892 SEND-> 0000 F4 44 0C E3 3B 13 05 00 29 1C 05 083 SEND-> 0000 F4 44 09 D2 7A 00

  00 00 484 SEND-> 0000 F4 44 10 DA 01 DB 6C 79 F6 05 02 27 35 01 00 005 SEND-> 0000 F4 44 05 DB 00

  第二個文件:1 SEND-> 0000 F4 44 1F 30 6C 79 F6 05 01 01 00 01 00 01 75 09SEND-> 0010 05 00 70 6B 00 00

  00 00 05 00 05 00 00 00 1A2 SEND-> 0000 F4 44 0C E3 3B 13 05 00 29 1C 05 843 SEND-> 0000 F4 44 09 D2 56 00

  00 00 484 SEND-> 0000 F4 44 10 DA 01 B8 6C 79 F6 05 02 27 35 01 00 005 SEND-> 0000 F4 44 05 DB 00

  哈,這一下兩個文件大部分都一樣啦,說明我們的推理是正確的,上面就是我們需要的明文!

  接下來就是搞清楚一些關鍵的字節所代表的含義,這就需要截獲大量的數據來分析。

  首先我們會發現每個數據包都是"F4 44"開頭,第3個字節是變化的,但是變化很有規律。我們來看看各個包的長度,

  發現什麼沒有?對了,第3個字節就是包的長度! 通過截獲大量的數據包,我們判斷第4個字節代表指令,也就是說客戶

  端告訴服務器進行的是什麼操作。例如向服務器請求戰鬥指令爲"30",戰鬥中移動指令爲"D4"等。 接下來,我們就需要

  分析一下上面第一個包"F4 44 1F 30 6C 79 F6 05 01 01 00 01 00 01 75 09 05 00 CF 26 00 00 00 00 05 00 1C 00

  00 00 89",在這個包裏包含什麼信息呢?應該有通知服務器你PK的哪個NPC吧,我們就先來找找這個店小二的代碼在什麼

  地方。 我們再PK一個小嘍羅(就是大理客棧外的那個咯):SEND-> 0000 F4 44 1F 30 D4 75 F6 05 01 01 00 01 00 01

  75 09SEND-> 0010 05 00 8A 19 00 00 00 00 11 00 02 00 00 00 C0 我們根據常理分析,遊戲裏的NPC種類雖然不會超

  過65535(FFFF),但開發時不會把自己限制在字的範圍,那樣不利於遊戲的擴充,所以我們在雙字裏看看。通過"店小二

  "和"小嘍羅"兩個包的對比,我們把目標放在"6C 79 F6 05"和"CF 26 00 00"上。(對比一下很容易的,但你不能太遲鈍

  咯,呵呵)我們再看看後面的包,在後面的包裏應該還會出現NPC的代碼,比如移動的包,遊戲允許觀戰,服務器必然需

  要知道NPC的移動座標,再廣播給觀戰的其他玩家。在後面第4個包"SEND-> 0000 F4 44 10 DA 01 DB 6C 79 F6 05 02 27

  35 01 00 00"裏我們又看到了"6C 79 F6 05",初步斷定店小二的代碼就是它了!(這分析裏邊包含了很多工作的,大家

  可以用WPE截下數據來自己分析分析)

  第一個包的分析暫時就到這裏(裏面還有的信息我們暫時不需要完全清楚了)

  我們看看第4個包"SEND-> 0000 F4 44 10 DA 01 DB 6C 79 F6 05 02 27 35 01 00 00",再截獲PK黃狗的包,(狗會

  出來2只哦)看看包的格式:SEND-> 0000 F4 44 1A DA 02 0B 4B 7D F6 05 02 27 35 01 00 00SEND-> 0010 EB 03 F8

  05 02 27 36 01 00 00

  根據上面的分析,黃狗的代碼爲"4B 7D F6 05"(100040011),不過兩隻黃狗服務器怎樣分辨呢?看看"EB 03 F8

  05"(100140011),是上一個代碼加上100000,呵呵,這樣服務器就可以認出兩隻黃狗了。我們再通過野外遇敵截獲的數

  據包來證實,果然如此。

  那麼,這個包的格式應該比較清楚了:第3個字節爲包的長度,"DA"爲指令,第5個字節爲NPC個數,從第7個字節開始

  的10個字節代表一個NPC的信息,多一個NPC就多10個字節來表示。

  大家如果玩過網金,必然知道隨機遇敵有時會出現增援,我們就利用遊戲這個增援來讓每次戰鬥都會出現增援的NPC

  吧。

  通過在戰鬥中出現增援截獲的數據包,我們會發現服務器端發送了這樣一個包:F4 44 12 E9 EB 03 F8 05 02 00 00

  03 00 00 00 00 00 00 第5-第8個字節爲增援NPC的代碼(這裏我們就簡單的以黃狗的代碼來舉例)。 那麼,我們就利用

  單機代理技術來同時欺騙客戶端和服務器吧!

  好了,呼叫NPC的工作到這裏算是完成了一小半,接下來的事情,怎樣修改封包和發送封包,我們下節繼續講解吧。

  四:怎麼冒充"客戶端"向"服務器"發我們需要的封包?

  這裏我們需要使用一個工具,它位於客戶端和服務器端之間,它的工作就是進行數據包的接收和轉發,這個工具我們

  稱爲代理。如果代理的工作單純就是接收和轉發的話,這就毫無意義了,但是請注意:所有的數據包都要通過它來傳輸,

  這裏的意義就重大了。我們可以分析接收到的數據包,或者直接轉發,或者修改後轉發,或者壓住不轉發,甚至僞造我們

  需要的封包來發送。

  下面我們繼續講怎樣來同時欺騙服務器和客戶端,也就是修改封包和僞造封包。 通過我們上節的分析,我們已經知

  道了打多個NPC的封包格式,那麼我們就動手吧!

  首先我們要查找客戶端發送的包,找到戰鬥的特徵,就是請求戰鬥的第1個包,我們找"F4 44 1F 30"這個特徵,這是

  不會改變的,當然是要解密後來查找哦。 找到後,表示客戶端在向服務器請求戰鬥,我們不動這個包,轉發。 繼續向下

  查找,這時需要查找的特徵碼不太好辦,我們先查找"DA",這是客戶端發送NPC信息的數據包的指令,那麼可能其他包也

  有"DA",沒關係,我們看前3個字節有沒有"F4 44"就行了。找到後,我們的工作就開始了!

  我們確定要打的NPC數量。這個數量不能很大,原因在於網金的封包長度用一個字節表示,那麼一個包可以有255個字

  節,我們上面分析過,增加一個NPC要增加10個字節,所以大家算算就知道,打20個NPC比較合適。

  然後我們要把客戶端原來的NPC代碼分析計算出來,因爲增加的NPC代碼要加上100000哦。再把我們增加的NPC代碼計

  算出來,並且組合成新的封包,注意代表包長度的字節要修改啊,然後轉發到服務器,這一步在編寫程序的時候要注意算

  法,不要造成較大延遲。

  上面我們欺騙服務器端完成了,欺騙客戶端就簡單了。

  發送了上面的封包後,我們根據新增NPC代碼構造封包馬上發給客戶端,格式就是"F4 44 12 E9 NPC代碼 02 00 00

  03 00 00 00 00 00 00",把每個新增的NPC都構造這樣一個包,按順序連在一起發送給客戶端,客戶端也就被我們騙過了

  ,很簡單吧。

  以後戰鬥中其他的事我們就不管了,盡情地開打吧。

  [本日誌由 looo 於 2005-02-10 01:21 PM 編輯]

  looo 於 2005-02-10 01:16 PM 發表評論:

  遊戲外掛基本原理及實現

  解釋遊戲外掛的基本原理和實現方法

  遊戲外掛已經深深地影響着衆多網絡遊戲玩家,今天在網上看到了一些關於遊戲外掛編寫的技術,於是轉載上供大家參考

  1、遊戲外掛的原理

  外掛現在分爲好多種,比如模擬鍵盤的,鼠標的,修改數據包的,還有修改本地內存的,但好像沒有修改服務器內存

  的哦,呵呵。其實修改服務器也是有辦法的,只是技術太高一般人沒有辦法入手而已。(比如請GM去夜總會、送禮、收黑

  錢等等辦法都可以修改服務器數據,哈哈)

  修改遊戲無非是修改一下本地內存的數據,或者截獲API函數等等。這裏我把所能想到的方法都作一個介紹,希望大

  家能做出很好的外掛來使遊戲廠商更好的完善自己的技術。我見到一篇文章是講魔力寶貝的理論分析,寫得不錯,大概是

  那個樣子。下來我就講解一下技術方面的東西,以作引玉之用。

  2 技術分析部分

  2.1 模擬鍵盤或鼠標的響應

  我們一般使用:

  UINT SendInput(

  UINT nInputs,   // count of input events

  PINPUT pInputs, // array of input events

  int cbSize    // size of structure

  );

  API函數。第一個參數是說明第二個參數的矩陣的維數的,第二個參數包含了響應事件,這個自己填充就可以,最後

  是這個結構的大小,非常簡單,這是最簡單的方法模擬鍵盤鼠標了,呵呵。注意,這個函數還有個替代函數:

  VOID keybd_event(

  BYTE bVk,       // 虛擬鍵碼

  BYTE bScan,      // 掃描碼

  DWORD dwFlags,

  ULONG_PTR dwExtraInfo // 附加鍵狀態

  );

  與

  VOID mouse_event(

  DWORD dwFlags,      // motion and click options

  DWORD dx,         // horizontal position or change

  DWORD dy,        // vertical position or change

  DWORD dwData,      // wheel movement

  ULONG_PTR dwExtraInfo  // application-defined information

  );

  這兩個函數非常簡單了,我想那些按鍵精靈就是用的這個吧。上面的是模擬鍵盤,下面的是模擬鼠標的。這個僅僅是

  模擬部分,要和遊戲聯繫起來我們還需要找到遊戲的窗口才行,或者包含快捷鍵,就象按鍵精靈的那個激活鍵一樣,我們

  可以用GetWindow函數來枚舉窗口,也可以用Findwindow函數來查找制定的窗口(注意,還有一個FindWindowEx),

  FindwindowEx可以找到窗口的子窗口,比如按鈕,等什麼東西。當遊戲切換場景的時候我們可以用FindWindowEx來確定一

  些當前窗口的特徵,從而判斷是否還在這個場景,方法很多了,比如可以GetWindowInfo來確定一些東西,比如當查找不

  到某個按鈕的時候就說明遊戲場景已經切換了,等等辦法。有的遊戲沒有控件在裏面,這是對圖像做座標變換的話,這種

  方法就要受到限制了。這就需要我們用別的辦法來輔助分析了。

  至於快捷鍵我們要用動態連接庫實現了,裏面要用到hook技術了,這個也非常簡單。大家可能都會了,其實就是一個

  全局的hook對象然後SetWindowHook就可以了,回調函數都是現成的,而且現在網上的例子多如牛毛。這個實現在外掛中

  已經很普遍了。如果還有誰不明白,那就去看看MSDN查找SetWindowHook就可以了。

  不要低估了這個動態連接庫的作用,它可以切入所有的進程空間,也就是可以加載到所有的遊戲裏面哦,只要用對,

  你會發現很有用途的。這個需要你複習一下Win32編程的基礎知識了。呵呵,趕快去看書吧。

  2.2 截獲消息

  有些遊戲的響應機制比較簡單,是基於消息的,或者用什麼定時器的東西。這個時候你就可以用攔截消息來實現一些

  有趣的功能了。

  我們攔截消息使用的也是hook技術,裏面包括了鍵盤消息,鼠標消息,系統消息,日誌等,別的對我們沒有什麼大的

  用處,我們只用攔截消息的回調函數就可以了,這個不會讓我寫例子吧。其實這個和上面的一樣,都是用SetWindowHook

  來寫的,看看就明白了很簡單的。

  至於攔截了以後做什麼就是你的事情了,比如在每個定時器消息裏面處理一些我們的數據判斷,或者在定時器裏面在

  模擬一次定時器,那麼有些數據就會處理兩次,呵呵。後果嘛,不一定是好事情哦,呵呵,不過如果數據計算放在客戶端

  的遊戲就可以真的改變數據了,呵呵,試試看吧。用途還有很多,自己想也可以想出來的,呵呵。

  2.3 攔截Socket包

  這個技術難度要比原來的高很多。

  首先我們要替換WinSock.DLL或者WinSock32.DLL,我們寫的替換函數要和原來的函數一致才行,就是說它的函數輸出

  什麼樣的,我們也要輸出什麼樣子的函數,而且參數,參數順序都要一樣才行,然後在我們的函數裏面調用真正的

  WinSock32.DLL裏面的函數就可以了。

  首先:我們可以替換動態庫到系統路徑。

  其次:我們應用程序啓動的時候可以加載原有的動態庫,用這個函數LoadLibary然後定位函數入口用GetProcAddress

  函數獲得每個真正Socket函數的入口地址。

  當遊戲進行的時候它會調用我們的動態庫,然後從我們的動態庫中處理完畢後才跳轉到真正動態庫的函數地址,這樣

  我們就可以在裏面處理自己的數據了,應該是一切數據。呵呵,興奮吧,攔截了數據包我們還要分析之後才能進行正確的

  應答,不要以爲這樣工作就完成了,還早呢。等分析完畢以後我們還要仿真應答機制來和服務器通信,一個不小心就會被

  封號。

  分析數據纔是工作量的來源呢,遊戲每次升級有可能加密方式會有所改變,因此我們寫外掛的人都是亡命之徒啊,被

  人愚弄了還不知道。

  2.4 截獲API

  上面的技術如果可以靈活運用的話我們就不用截獲API函數了,其實這種技術是一種補充技術。比如我們需要截獲

  Socket以外的函數作爲我們的用途,我們就要用這個技術了,其實我們也可以用它直接攔截在Socket中的函數,這樣更直

  接。

  現在攔截API的教程到處都是,我就不列舉了,我用的比較習慣的方法是根據輸入節進行攔截的,這個方法可以用到

  任何一種操作系統上,比如Windows 98/2000等,有些方法不是跨平臺的,我不建議使用。這個技術大家可以參考

  《Windows核心編程》裏面的545頁開始的內容來學習,如果是Win98系統可以用“Windows系統奧祕”那個最後一章來學習

  。

  網絡遊戲通訊模型初探

  [文章導讀]

  本文就將圍繞三個主題來給大家講述一下網絡遊戲的網絡互連實現方法

  序言

  網絡遊戲,作爲遊戲與網絡有機結合的產物,把玩家帶入了新的娛樂領域。網絡遊戲在中國開始發展至今也僅有3,4

  年的歷史,跟已經擁有幾十年開發歷史的單機遊戲相比,網絡遊戲還是非常年輕的。當然,它的形成也是根據歷史變化而

  產生的可以說沒有互聯網的興起,也就沒有網絡遊戲的誕生。作爲新興產物,網絡遊戲的開發對廣大開發者來說更加神祕

  ,對於一個未知領域,開發者可能更需要了解的是網絡遊戲與普通單機遊戲有何區別,網絡遊戲如何將玩家們連接起來,

  以及如何爲玩家提供一個互動的娛樂環境。本文就將圍繞這三個主題來給大家講述一下網絡遊戲的網絡互連實現方法。

  網絡遊戲與單機遊戲

  說到網絡遊戲,不得不讓人聯想到單機遊戲,實際上網絡遊戲的實質脫離不了單機遊戲的製作思想,網絡遊戲和單機

  遊戲的差別大家可以很直接的想到:不就是可以多人連線嗎?沒錯,但如何實現這些功能,如何把網絡連線合理的融合進

  單機遊戲,就是我們下面要討論的內容。在瞭解網絡互連具體實現之前,我們先來了解一下單機與網絡遊戲它們各自的運

  行流程,只有瞭解這些,你才能深入網絡遊戲開發的核心。

  現在先讓我們來看一下普通單機遊戲的簡化執行流程:

  Initialize() // 初始化模塊

  {

  初始化遊戲數據;

  }

  Game() // 遊戲循環部分

  {

  繪製遊戲場景、人物以及其它元素;

  獲取用戶操作輸入;

  switch( 用戶輸入數據)

  {

  case 移動:

  {

  處理人物移動;

  }

  break;

  case 攻擊:

  {

  處理攻擊邏輯:

  }

  break;

  ...

  其它處理響應;

  ...

  default:

  break;

  }

  遊戲的NPC等邏輯AI處理;

  }

  Exit() // 遊戲結束

  {

  釋放遊戲數據;

  離開遊戲;

  }

  我們來說明一下上面單機遊戲的流程。首先,不管是遊戲軟件還是其他應用軟件,初始化部分必不可少,這裏需要對

  遊戲的數據進行初始化,包括圖像、聲音以及一些必備的數據。接下來,我們的遊戲對場景、人物以及其他元素進行循環

  繪製,把遊戲世界展現給玩家,同時接收玩家的輸入操作,並根據操作來做出響應,此外,遊戲還需要對NPC以及一些邏

  輯AI進行處理。最後,遊戲數據被釋放,遊戲結束。

  網絡遊戲與單機遊戲有一個很顯着的差別,就是網絡遊戲除了一個供操作遊戲的用戶界面平臺(如單機遊戲)外,還需

  要一個用於連接所有用戶,併爲所有用戶提供數據服務的服務器,從某些角度來看,遊戲服務器就像一個大型的數據庫,

  提供數據以及數據邏輯交互的功能。讓我們來看看一個簡單的網絡遊戲模型執行流程:

  客戶機:

  Login()// 登入模塊

  {

  初始化遊戲數據;

  獲取用戶輸入的用戶和密碼;

  與服務器創建網絡連接;

  發送至服務器進行用戶驗證;

  ...

  等待服務器確認消息;

  ...

  獲得服務器反饋的登入消息;

  if( 成立 )

  進入遊戲;

  else

  提示用戶登入錯誤並重新接受用戶登入;

  }

  Game()// 遊戲循環部分

  {

  繪製遊戲場景、人物以及其它元素;

  獲取用戶操作輸入;

  將用戶的操作發送至服務器;

  ...

  等待服務器的消息;

  ...

  接收服務器的反饋信息;

  switch( 服務器反饋的消息數據 )

  {

  case 本地玩家移動的消息:

  {

  if( 允許本地玩家移動 )

  客戶機處理人物移動;

  else

  客戶機保持原有狀態;

  }

  break;

  case 其他玩家/NPC的移動消息:

  {

  根據服務器的反饋信息進行其他玩家或者NPC的移動處理;

  }

  break;

  case 新玩家加入遊戲:

  {

  在客戶機中添加顯示此玩家;

  }

  break;

  case 玩家離開遊戲:

  {

  在客戶機中銷燬此玩家數據;

  }

  break;

  ...

  其它消息類型處理;

  ...

  default:

  break;

  }

  }

  Exit()// 遊戲結束

  {

  發送離開消息給服務器;

  ...

  等待服務器確認;

  ...

  得到服務器確認消息;

  與服務器斷開連接;

  釋放遊戲數據;

  離開遊戲;

  }

  服務器:

  Listen()  // 遊戲服務器等待玩家連接模塊

  {

  ...

  等待用戶的登入信息;

  ...

  接收到用戶登入信息;

  分析用戶名和密碼是否符合;

  if( 符合 )

  {

  發送確認允許進入遊戲消息給客戶機;

  把此玩家進入遊戲的消息發佈給場景中所有玩家;

  把此玩家添加到服務器場景中;

  }

  else

  {

  斷開與客戶機的連接;

  }

  }

  Game() // 遊戲服務器循環部分

  {

  ...

  等待場景中玩家的操作輸入;

  ...

  接收到某玩家的移動輸入或NPC的移動邏輯輸入;

  // 此處只以移動爲例

  進行此玩家/NPC在地圖場景是否可移動的邏輯判斷;

  if( 可移動 )

  {

  對此玩家/NPC進行服務器移動處理;

  發送移動消息給客戶機;

  發送此玩家的移動消息給場景上所有玩家;

  }

  else

  發送不可移動消息給客戶機;

  }

  Exit()  // 遊戲服務=器結束

  {

  接收到玩家離開消息;

  將此消息發送給場景中所有玩家;

  發送允許離開的信息;

  將玩家數據存入數據庫;

  註銷此玩家在服務器內存中的數據;

  }

  }

  讓我們來說明一下上面簡單網絡遊戲模型的運行機制。先來講講服務器端,這裏服務器端分爲三個部分(實際上一個

  完整的網絡遊戲遠不止這些):登入模塊、遊戲模塊和登出模塊。登入模塊用於監聽網絡遊戲客戶端發送過來的網絡連接

  消息,並且驗證其合法性,然後在服務器中創建這個玩家並且把玩家帶領到遊戲模塊中; 遊戲模塊則提供給玩家用戶實

  際的應用服務,我們在後面會詳細介紹這個部分; 在得到玩家要離開遊戲的消息後,登出模塊則會把玩家從服務器中刪

  除,並且把玩家的屬性數據保存到服務器數據庫中,如: 經驗值、等級、生命值等。

  接下來讓我們看看網絡遊戲的客戶端。這時候,客戶端不再像單機遊戲一樣,初始化數據後直接進入遊戲,而是在與

  服務器創建連接,並且獲得許可的前提下才進入遊戲。除此之外,網絡遊戲的客戶端遊戲進程需要不斷與服務器進行通訊

  ,通過與服務器交換數據來確定當前遊戲的狀態,例如其他玩家的位置變化、物品掉落情況。同樣,在離開遊戲時,客戶

  端會向服務器告知此玩家用戶離開,以便於服務器做出相應處理。

  以上用簡單的僞代碼給大家闡述了單機遊戲與網絡遊戲的執行流程,大家應該可以清楚看出兩者的差別,以及兩者間相互

  的關係。我們可以換個角度考慮,網絡遊戲就是把單機遊戲的邏輯運算部分搬移到遊戲服務器中進行處理,然後把處理結

  果(包括其他玩家數據)通過遊戲服務器返回給連接的玩家。

  網絡互連

  在瞭解了網絡遊戲基本形態之後,讓我們進入真正的實際應用部分。首先,作爲網絡遊戲,除了常規的單機遊戲所必

  需的東西之外,我們還需要增加一個網絡通訊模塊,當然,這也是網絡遊戲較爲主要的部分,我們來討論一下如何實現網

  絡的通訊模塊。

  一個完善的網絡通訊模塊涉及面相當廣,本文僅對較爲基本的處理方式進行討論。網絡遊戲是由客戶端和服務器組成

  ,相應也需要兩種不同的網絡通訊處理方式,不過也有相同之處,我們先就它們的共同點來進行介紹。我們這裏以

  Microsoft Windows 2000 [2000 Server]作爲開發平臺,並且使用Winsock作爲網絡接口(可能一些朋友會考慮使用

  DirectPlay來進行網絡通訊,不過對於當前在線遊戲,DirectPlay並不適合,具體原因這裏就不做討論了)。

  確定好平臺與接口後,我們開始進行網絡連接創建之前的一些必要的初始化工作,這部分無論是客戶端或者服務器都

  需要進行。讓我們看看下面的代碼片段:

  WORD wVersionRequested;

  WSADATAwsaData;

  wVersionRequested MAKEWORD(1, 1);

  if( WSAStartup( wVersionRequested, &wsaData ) !0 )

  {

  Failed( WinSock Version Error!" );

  }

  上面通過調用Windows的socket API函數來初始化網絡設備,接下來進行網絡Socket的創建,代碼片段如下:

  SOCKET sSocket socket( AF_INET, m_lProtocol, 0 );

  if( sSocket == INVALID_SOCKET )

  {

  Failed( "WinSocket Create Error!" );

  }

  這裏需要說明,客戶端和服務端所需要的Socket連接數量是不同的,客戶端只需要一個Socket連接足以滿足遊戲的需

  要,而服務端必須爲每個玩家用戶創建一個用於通訊的Socket連接。當然,並不是說如果服務器上沒有玩家那就不需要創

  建Socket連接,服務器端在啓動之時會生成一個特殊的Socket用來對玩家創建與服務器連接的請求進行響應,等介紹網絡

  監聽部分後會有更詳細說明。

  有初始化與創建必然就有釋放與刪除,讓我們看看下面的釋放部分:

  if( sSocket != INVALID_SOCKET )

  {

  closesocket( sSocket );

  }

  if( WSACleanup() != 0 )

  {

  Warning( "Can't release Winsocket" );

  }

  這裏兩個步驟分別對前面所作的創建初始化進行了相應釋放。

  接下來看看服務器端的一個網絡執行處理,這裏我們假設服務器端已經創建好一個Socket供使用,我們要做的就是讓

  這個Socket變成監聽網絡連接請求的專用接口,看看下面代碼片段:

  SOCKADDR_IN addr;

  memset( &addr, 0, sizeof(addr) );

  addr.sin_family = AF_INET;

  addr.sin_addr.s_addr = htonl( INADDR_ANY );

  addr.sin_port = htons( Port );  // Port爲要監聽的端口號

  // 綁定socket

  if( bind( sSocket, (SOCKADDR*)&addr, sizeof(addr) ) == SOCKET_ERROR )

  {

  Failed( "WinSocket Bind Error!");

  }

  // 進行監聽

  if( listen( sSocket, SOMAXCONN ) == SOCKET_ERROR )

  {

  Failed( "WinSocket Listen Error!");

  }

  這裏使用的是阻塞式通訊處理,此時程序將處於等待玩家用戶連接的狀態,倘若這時候有客戶端連接進來,則通過

  accept()來創建針對此玩家用戶的Socket連接,代碼片段如下:

  sockaddraddrServer;

  int nLen sizeof( addrServer );

  SOCKET sPlayerSocket accept( sSocket, &addrServer, &nLen );

  if( sPlayerSocket == INVALID_SOCKET )

  {

  Failed( WinSocket Accept Error!");

  }

  這裏我們創建了sPlayerSocket連接,此後遊戲服務器與這個玩家用戶的通訊全部通過此Socket進行,到這裏爲止,

  我們服務器已經有了接受玩家用戶連接的功能,現在讓我們來看看遊戲客戶端是如何連接到遊戲服務器上,代碼片段如下

  :

  SOCKADDR_IN addr;

  memset( &addr, 0, sizeof(addr) );

  addr.sin_family = AF_INET;// 要連接的遊戲服務器端口號

  addr.sin_addr.s_addr = inet_addr( IP );// 要連接的遊戲服務器IP地址,

  addr.sin_port = htons( Port );//到此,客戶端和服務器已經有了通訊的橋樑,

  //接下來就是進行數據的發送和接收:

  connect( sSocket, (SOCKADDR*)&addr, sizeof(addr) );

  if( send( sSocket, pBuffer, lLength, 0 ) == SOCKET_ERROR )

  {

  Failed( "WinSocket Send Error!");

  }

  這裏的pBuffer爲要發送的數據緩衝指針,lLength爲需要發送的數據長度,通過這支Socket API函數,我們無論在客

  戶端或者服務端都可以進行數據的發送工作,同時,我們可以通過recv()這支Socket API函數來進行數據接收:

  if( recv( sSocket, pBuffer, lLength, 0 ) == SOCKET_ERROR )

  {

  Failed( "WinSocket Recv Error!");

  }

  其中pBuffer用來存儲獲取的網絡數據緩衝,lLength則爲需要獲取的數據長度。

  現在,我們已經瞭解了一些網絡互連的基本知識,但作爲網絡遊戲,如此簡單的連接方式是無法滿足網絡遊戲中百人

  千人同時在線的,我們需要更合理容錯性更強的網絡通訊處理方式,當然,我們需要先了解一下網絡遊戲對網絡通訊的需

  求是怎樣的。

  大家知道,遊戲需要不斷循環處理遊戲中的邏輯並進行遊戲世界的繪製,上面所介紹的Winsock處理方式均是以阻塞

  方式進行,這樣就違背了遊戲的執行本質,可以想象,在客戶端連接到服務器的過程中,你的遊戲不能得到控制,這時如

  果玩家想取消連接或者做其他處理,甚至顯示一個最基本的動態連接提示都不行。

  所以我們需要用其他方式來處理網絡通訊,使其不會與遊戲主線相沖突,可能大家都會想到: 創建一個網絡線程來

  處理不就可以了?沒錯,我們可以創建一個專門用於網絡通訊的子線程來解決這個問題。當然,我們遊戲中多了一個線程

  ,我們就需要做更多的考慮,讓我們來看看如何創建網絡通訊線程。

  在Windows系統中,我們可以通過CreateThread()函數來進行線程的創建,看看下面的代碼片段:

  DWORD dwThreadID;

  HANDLE hThread = CreateThread( NULL, 0, NetThread/*網絡線程函式*/, sSocket, 0, &dwThreadID );

  if( hThread == NULL )

  {

  Failed( "WinSocket Thread Create Error!");

  }

  這裏我們創建了一個線程,同時將我們的Socket傳入線程函數:

  DWORD WINAPINetThread(LPVOID lParam)

  {

  SOCKET sSocket (SOCKET)lParam;

  ...

  return 0;

  }

  NetThread就是我們將來用於處理網絡通訊的網絡線程。那麼,我們又如何把Socket的處理引入線程中?

  看看下面的代碼片段:

  HANDLE hEvent;

  hEvent = CreateEvent(NULL,0,0,0);

  // 設置異步通訊

  if( WSAEventSelect( sSocket, hEvent,

  FD_ACCEPT|FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE ) ==SOCKET_ERROR )

  {

  Failed( "WinSocket EventSelect Error!");

  }

  通過上面的設置之後,WinSock API函數均會以非阻塞方式運行,也就是函數執行後會立即返回,這時網絡通訊會以

  事件方式存儲於hEvent,而不會停頓整支程式。

  完成了上面的步驟之後,我們需要對事件進行響應與處理,讓我們看看如何在網絡線程中獲得網絡通訊所產生的事件

  消息:

  WSAEnumNetworkEvents( sSocket, hEvent, &SocketEvents );

  if( SocketEvents.lNetworkEvents != 0 )

  {

  switch( SocketEvents.lNetworkEvents )

  {

  case FD_ACCEPT:

  WSANETWORKEVENTS SocketEvents;

  break;

  case FD_CONNECT:

  {

  if( SocketEvents.iErrorCode[FD_CONNECT_BIT] == 0)

  // 連接成功

  {

  // 連接成功後通知主線程(遊戲線程)進行處理

  }

  }

  break;

  case FD_READ:

  // 獲取網絡數據

  {

  if( recv( sSocket, pBuffer, lLength, 0) == SOCKET_ERROR )

  {

  Failed( "WinSocket Recv Error!");

  }

  }

  break;

  case FD_WRITE:

  break;

  case FD_CLOSE:

  // 通知主線程(遊戲線程), 網絡已經斷開

  break;

  default:

  break;

  }

  }

  這裏僅對網絡連接(FD_CONNECT) 和讀取數據(FD_READ) 進行了簡單模擬操作,但實際中網絡線程接收到事件消息後

  ,會對數據進行組織整理,然後再將數據回傳給我們的遊戲主線程使用,遊戲主線程再將處理過的數據發送出去,這樣一

  個往返就構成了我們網絡遊戲中的數據通訊,是讓網絡遊戲動起來的最基本要素。

  最後,我們來談談關於網絡數據包(數據封包)的組織,網絡遊戲的數據包是遊戲數據通訊的最基本單位,網絡遊戲

  一般不會用字節流的方式來進行數據傳輸,一個數據封包也可以看作是一條消息指令,在遊戲進行中,服務器和客戶端會

  不停的發送和接收這些消息包,然後將消息包解析轉換爲真正所要表達的指令意義並執行。

  互動與管理

  說到互動,對於玩家來說是與其他玩家的交流,但對於計算機而言,實現互動也就是實現數據消息的相互傳遞。前面

  我們已經瞭解過網絡通訊的基本概念,它構成了互動的最基本條件,接下來我們需要在這個網絡層面上進行數據的通訊。

  遺憾的是,計算機並不懂得如何表達玩家之間的交流,因此我們需要提供一套可讓計算機瞭解的指令組織和解析機制,也

  就是對我們上面簡單提到的網絡數據包(數據封包)的處理機制。

  爲了能夠更簡單的給大家闡述網絡數據包的組織形式,我們以一個聊天處理模塊來進行討論,看看下面的代碼結構:

  struct tagMessage{

  long lType;

  long lPlayerID;

  };

  // 消息指令

  // 指令相關的玩家標識

  char strTalk[256]; // 消息內容

  上面是抽象出來的一個極爲簡單的消息包結構,我們先來談談其各個數據域的用途:

  首先,lType 是消息指令的類型,這是最爲基本的消息標識,這個標識用來告訴服務器或客戶端這條指令的具體用途

  ,以便於服務器或客戶端做出相應處理。lPlayerID 被作爲玩家的標識。大家知道,一個玩家在機器內部實際上也就是一

  堆數據,特別是在遊戲服務器中,可能有成千上萬個玩家,這時候我們需要一個標記來區分玩家,這樣就可以迅速找到特

  定玩家,並將通訊數據應用於其上。

  strTalk 是我們要傳遞的聊天數據,這部分纔是真正的數據實體,前面的參數只是數據實體應用範圍的限定。

  在組織完數據之後,緊接着就是把這個結構體數據通過Socket 連接發送出去和接收進來。這裏我們要了解,網絡在

  進行數據傳輸過程中,它並不關心數據採用的數據結構,這就需要我們把數據結構轉換爲二進制數據碼進行發送,在接收

  方,我們再將這些二進制數據碼轉換回程序使用的相應數據結構。讓我們來看看如何實現:

  tagMessageMsg;

  Msg.lTypeMSG_CHAT;

  Msg.lPlayerID 1000;

  strcpy( &Msg.strTalk, "聊天信息" );

  首先,我們假設已經組織好一個數據包,這裏MSG_CHAT 是我們自行定義的標識符,當然,這個標識符在服務器和客

  戶端要統一。玩家的ID 則根據遊戲需要來進行設置,這裏1000 只作爲假設,現在繼續:

  char* p = (char*)&Msg;

  long lLength = sizeof( tagMessage );

  send( sSocket, p, lLength );

  // 獲取數據結構的長度

  我們通過強行轉換把結構體轉變爲char 類型的數據指針,這樣就可以通過這個指針來進行流式數據處理,這裏通過

  sizeof() 獲得結構體長度,然後用WinSock 的Send() 函數將數據發送出去。

  接下來看看如何接收數據:

  long lLength = sizeof( tagMessage );

  char* Buffer = new char[lLength];

  recv( sSocket, Buffer, lLength );

  tagMessage* p = (tagMessage*)Buffer;

  // 獲取數據

  在通過WinSock 的recv() 函數獲取網絡數據之後,我們同樣通過強行轉換把獲取出來的緩衝數據轉換爲相應結構體

  ,這樣就可以方便地對數據進行訪問。(注:強行轉換僅僅作爲數據轉換的一種手段,實際應用中有更多可選方式,這裏

  只爲簡潔地說明邏輯)談到此處,不得不提到服務器/ 客戶端如何去篩選處理各種消息以及如何對通訊數據包進行管理。

  無論是服務器還是客戶端,在收到網絡消息的時候,通過上面的數據解析之後,還必須對消息類型進行一次篩選和派分,

  簡單來說就是類似Windows 的消息循環,不同消息進行不同處理。這可以通過一個switch 語句(熟悉Windows 消息循環

  的朋友相信已經明白此意),基於消

  息封包裏的lType 信息,對消息進行區分處理,考慮如下代碼片段:

  switch( p->lType ) // 這裏的p->lType爲我們解析出來的消息類型標識

  {

  case MSG_CHAT: // 聊天消息

  break;

  case MSG_MOVE: // 玩家移動消息

  break;

  case MSG_EXIT: // 玩家離開消息

  break;

  default:

  break;

  }

  上面片段中的MSG_MOVE 和MSG_EXIT 都是我們虛擬的消息標識(一個真實遊戲中的標識可能會有上百個,這就需要考

  慮優化和優先消息處理問題)。此外,一個網絡遊戲服務器面對的是成百上千的連接用戶,我們還需要一些合理的數據組

  織管理方式來進行相關處理。普通的單體遊戲服務器,可能會因爲當機或者用戶過多而導致整個遊戲網絡癱瘓,而這也就

  引入分組服務器機制,我們把服務器分開進行數據的分佈式處理。

  我們把每個模塊提取出來,做成專用的服務器系統,然後建立一個連接所有服務器的數據中心來進行數據交互,這裏

  每個模塊均與數據中心創建了連接,保證了每個模塊的相關性,同時玩家轉變爲與當前提供服務的服務器進行連接通訊,

  這樣就可以緩解單獨一臺服務器所承受的負擔,把壓力分散到多臺服務器上,同時保證了數據的統一,而且就算某臺服務

  器因爲異常而當機也不會影響其他模塊的遊戲玩家,從而提高了整體穩定性。

  分組式服務器緩解了服務器的壓力,但也帶來了服務器調度問題,分組式服務器需要對服務器跳轉進行處理,就以一

  個玩家進行遊戲場景跳轉作爲討論基礎:假設有一玩家處於遊戲場景A,他想從場景A 跳轉到場景B,在遊戲中,我們稱之

  場景切換,這時玩家就會觸發跳轉需求,比如走到了場景中的切換點,這樣服務器就把玩家數據從"遊戲場景A 服務器"刪

  除,同時在"遊戲場景B 服務器"中把玩家建立起來。

  這裏描述了場景切換的簡單模型,當中處理還有很多步驟,不過通過這樣的思考相信大家可以派生出很多應用技巧。

  不過需要注意的是,在場景切換或者說模塊間切換的時候,需要切實考慮好數據的傳輸安全以及邏輯合理性,否則切換很

  可能會成爲將來玩家複製物品的橋樑。

  總結

  本篇講述的都是通過一些簡單的過程來進行網絡遊戲通訊,提供了一個製作的思路,雖然具體實現起來還有許多要做

  ,但只要順着這個思路去擴展、去完善,相信大家很快就能夠編寫出自己的網絡通訊模塊。由於時間倉促,本文在很多細

  節方面都有省略,文中若有錯誤之處也望大家見諒。

發佈了15 篇原創文章 · 獲贊 20 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章