• <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>
            教父的告白
            一切都是紙老虎
            posts - 82,  comments - 7,  trackbacks - 0
            原文:http://game.chinaitlab.com/freshmen/783449.html

                要想在修改游戲中做到百戰(zhàn)百勝,是需要相當(dāng)豐富的計算機(jī)知識的。有很多計算機(jī)高手就是從玩游戲,修改游戲中,逐步對計算機(jī)產(chǎn)生濃厚的興趣,逐步成長起來 的。不要在羨慕別人能夠做到的,因為別人能夠做的你也能夠!我相信你們看了本教程后,會對游戲有一個全新的認(rèn)識,呵呵,因為我是個好老師!(別拿雞蛋砸我 呀,救命啊!#¥%……*)

                不過要想從修改游戲中學(xué)到知識,增加自己的計算機(jī)水平,可不能只是靠修改游戲呀! 要知道,修改游戲只是一個驗證你對你所了解的某些計算機(jī)知識的理解程度的場所,只能給你一些發(fā)現(xiàn)問題、解決問題的機(jī)會,只能起到幫助你提高學(xué)習(xí)計算機(jī)的興 趣的作用,而決不是學(xué)習(xí)計算機(jī)的捷徑。

                一:什么叫外掛?

                現(xiàn)在的網(wǎng)絡(luò)游戲多是基于Internet上客戶/服務(wù)器模式,服務(wù)端程序運(yùn)行在游戲服務(wù)器上,游戲的設(shè)計者在其中創(chuàng)造一個龐大的游戲空間,各地的玩家可以通過運(yùn)行客戶端程序同時登錄到游戲中。簡單地說,網(wǎng)絡(luò)游戲?qū)嶋H上就是由游戲開發(fā)商 提供一個游戲環(huán)境,而玩家們就是在這個環(huán)境中相對自由和開放地進(jìn)行游戲操作。那么既然在網(wǎng)絡(luò)游戲中有了服務(wù)器這個概念,我們以前傳統(tǒng)的修改游戲方法就顯得 無能為力了。記得我們在單機(jī)版的游戲中,隨心所欲地通過內(nèi)存搜索來修改角色的各種屬性,這在網(wǎng)絡(luò)游戲中就沒有任何用處了。因為我們在網(wǎng)絡(luò)游戲中所扮演角色 的各種屬性及各種重要資料都存放在服務(wù)器上,在我們自己機(jī)器上(客戶端)只是顯示角色的狀態(tài),所以通過修改客戶端內(nèi)存里有關(guān)角色的各種屬性是不切實(shí)際的。 那么是否我們就沒有辦法在網(wǎng)絡(luò)游戲中達(dá)到我們修改的目的?回答是"否".我們知道Internet客戶/服務(wù)器模式的通訊一般采用TCP/IP通信協(xié)議,數(shù)據(jù)交換是通過IP數(shù)據(jù)包的傳輸來實(shí)現(xiàn)的,一般來說我們客戶端向服務(wù)器發(fā)出某些請求,比如移動、戰(zhàn)斗等指令都是通過封包的形式和服務(wù)器交換數(shù) 據(jù)。那么我們把本地發(fā)出消息稱為SEND,意思就是發(fā)送數(shù)據(jù),服務(wù)器收到我們SEND的消息后,會按照既定的程序把有關(guān)的信息反饋給客戶端,比如,移動的 坐標(biāo),戰(zhàn)斗的類型。那么我們把客戶端收到服務(wù)器發(fā)來的有關(guān)消息稱為RECV.知道了這個道理,接下來我們要做的工作就是分析客戶端和服務(wù)器之間往來的數(shù)據(jù) (也就是封包),這樣我們就可以提取到對我們有用的數(shù)據(jù)進(jìn)行修改,然后模擬服務(wù)器發(fā)給客戶端,或者模擬客戶端發(fā)送給服務(wù)器,這樣就可以實(shí)現(xiàn)我們修改游戲的 目的了。

                目前除了修改游戲封包來實(shí)現(xiàn)修改游戲的目的,我們也可以修改客戶端的有關(guān)程序來達(dá)到我們的要求。我們知道目前各個服務(wù)器的運(yùn)算能力是有限的,特別在游戲 中,游戲服務(wù)器要計算游戲中所有玩家的狀況幾乎是不可能的,所以有一些運(yùn)算還是要依靠我們客戶端來完成,這樣又給了我們修改游戲提供了一些便利。比如我們 可以通過將客戶端程序脫殼來發(fā)現(xiàn)一些程序的判斷分支,通過跟蹤調(diào)試我們可以把一些對我們不利的判斷去掉,以此來滿足我們修改游戲的需求。 在下幾個章節(jié)中,我們將給大家講述封包的概念,和修改跟蹤客戶端的有關(guān)知識。大家準(zhǔn)備好了嗎?

                游戲數(shù)據(jù)格式和存儲

                在進(jìn)行我們的工作之前,我們需要掌握一些關(guān)于計算機(jī)中儲存數(shù)據(jù)方式的知識和游戲中儲存數(shù)據(jù)的特點(diǎn)。本章節(jié)是提供給菜鳥級的玩家看的,如果你是高手就可以跳 過了,如果,你想成為無堅不摧的劍客,那么,這些東西就會花掉你一些時間;如果,你只想作個江湖的游客的話,那么這些東西,了解與否無關(guān)緊要。是作劍客, 還是作游客,你選擇吧!

                現(xiàn)在我們開始!首先,你要知道游戲中儲存數(shù)據(jù)的幾種格式,這幾種格式是:字節(jié)(BYTE)、字(WORD)和雙字(DOUBLE WORD),或者說是8位、16位和32位儲存方式。字節(jié)也就是8位方式能儲存0~255的數(shù)字;字或說是16位儲存方式能儲存0~65535的數(shù);雙字 即32位方式能儲存0~4294967295的數(shù)。

                為何要了解這些知識呢?在游戲中各種參數(shù)的最大值是不同的,有些可能100左右就夠了,比如,金庸群俠傳中的角色的等級、隨機(jī)遇敵個數(shù)等等。而有些卻需要 大于255甚至大于65535,象金庸群俠傳中角色的金錢值可達(dá)到數(shù)百萬。所以,在游戲中各種不同的數(shù)據(jù)的類型是不一樣的。在我們修改游戲時需要尋找準(zhǔn)備 修改的數(shù)據(jù)的封包,在這種時候,正確判斷數(shù)據(jù)的類型是迅速找到正確地址的重要條件。

                在計算機(jī)中數(shù)據(jù)以字節(jié)為基本的儲存單位,每個字節(jié)被賦予一個編號,以確定各自的位置。這個編號我們就稱為地址。

                在需要用到字或雙字時,計算機(jī)用連續(xù)的兩個字節(jié)來組成一個字,連續(xù)的兩個字組成一個雙字。而一個字或雙字的地址就是它們的低位字節(jié)的地址。 現(xiàn)在我們常用的Windows 9x操作系統(tǒng)中,地址是用一個32位的二進(jìn)制數(shù)表示的。而在平時我們用到內(nèi)存地址時,總是用一個8位的16進(jìn)制數(shù)來表示它。

                二進(jìn)制和十六進(jìn)制又是怎樣一回事呢?

                簡單說來,二進(jìn)制數(shù)就是一種只有0和1兩個數(shù)碼,每滿2則進(jìn)一位的計數(shù)進(jìn)位法。同樣,16進(jìn)制就是每滿十六就進(jìn)一位的計數(shù)進(jìn)位法。16進(jìn)制有0——F十六 個數(shù)字,它為表示十到十五的數(shù)字采用了A、B、C、D、E、F六個數(shù)字,它們和十進(jìn)制的對應(yīng)關(guān)系是:A對應(yīng)于10,B對應(yīng)于11,C對應(yīng)于12,D對應(yīng)于 13,E對應(yīng)于14,F(xiàn)對應(yīng)于15.而且,16進(jìn)制數(shù)和二進(jìn)制數(shù)間有一個簡單的對應(yīng)關(guān)系,那就是;四位二進(jìn)制數(shù)相當(dāng)于一位16進(jìn)制數(shù)。比如,一個四位的二 進(jìn)制數(shù)1111就相當(dāng)于16進(jìn)制的F,1010就相當(dāng)于A.了解這些基礎(chǔ)知識對修改游戲有著很大的幫助,下面我就要談到這個問題。由于在計算機(jī)中數(shù)據(jù)是以 二進(jìn)制的方式儲存的,同時16進(jìn)制數(shù)和二進(jìn)制間的轉(zhuǎn)換關(guān)系十分簡單,所以大部分的修改工具在顯示計算機(jī)中的數(shù)據(jù)時會顯示16進(jìn)制的代碼,而且在你修改時也 需要輸入16進(jìn)制的數(shù)字。你清楚了吧?

                在游戲中看到的數(shù)據(jù)可都是十進(jìn)制的,在要尋找并修改參數(shù)的值時,可以使用Windows提供的計算器來進(jìn)行十進(jìn)制和16進(jìn)制的換算,我們可以在開始菜單里的程序組中的附件中找到它。

                現(xiàn)在要了解的知識也差不多了!不過,有個問題在游戲修改中是需要注意的。在計算機(jī)中數(shù)據(jù)的儲存方式一般是低位數(shù)儲存在低位字節(jié),高位數(shù)儲存在高位字節(jié)。比如,十進(jìn)制數(shù)41715轉(zhuǎn)換為16進(jìn)制的數(shù)為A2F3,但在計算機(jī)中這個數(shù)被存為F3A2.

                看了以上內(nèi)容大家對數(shù)據(jù)的存貯和數(shù)據(jù)的對應(yīng)關(guān)系都了解了嗎? 好了,接下來我們要告訴大家在游戲中,封包到底是怎么一回事了,來!大家把袖口卷起來,讓我們來干活吧!

                二:什么是封包?

                怎么截獲一個游戲的封包?怎么去檢查游戲服務(wù)器的ip地址和端口號? Internet用戶使用的各種信息服務(wù),其通訊的信息最終均可以歸結(jié)為以IP包為單位的信息傳送,IP包除了包括要傳送的數(shù)據(jù)信息外,還包含有信息要發(fā) 送到的目的IP地址、信息發(fā)送的源IP地址、以及一些相關(guān)的控制信息。當(dāng)一臺路由器收到一個IP數(shù)據(jù)包時,它將根據(jù)數(shù)據(jù)包中的目的IP地址項查找路由表,根據(jù)查找的結(jié)果將此IP數(shù)據(jù)包送往對應(yīng)端口。下一臺IP路由器收到此數(shù)據(jù)包后繼續(xù)轉(zhuǎn)發(fā),直至發(fā)到目的地。路由器之間可以通過路由協(xié)議來進(jìn)行路由信息的交換,從而更新路由表。

                那么我們所關(guān)心的內(nèi)容只是IP包中的數(shù)據(jù)信息,我們可以使用許多監(jiān)聽網(wǎng)絡(luò)的工具來截獲客戶端與服務(wù)器之間的交換數(shù)據(jù),下面就向你介紹其中的一種工具:WPE. WPE使用方法:執(zhí)行WPE會有下列幾項功能可選擇:

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

                TRACE追蹤功能。用來追蹤擷取程式送收的封包。WPE必須先完成點(diǎn)選欲追蹤的程式名稱,才可以使用此項目。 按下Play鍵開始擷取程式收送的封包。您可以隨時按下 | | 暫停追蹤,想繼續(xù)時請再按下 | | .按下正方形可以停止擷取封包并且顯示所有已擷取封包內(nèi)容。若您沒按下正方形停止鍵,追蹤的動作將依照OPTION里的設(shè)定值自動停止。如果您沒有擷取到 資料,試試將OPTION里調(diào)整為Winsock Version 2.WPE 及 Trainers 是設(shè)定在顯示至少16 bits 顏色下才可執(zhí)行。

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

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

                TRAINER MAKER制作修改器。

                OPTIONS設(shè)定功能。讓您調(diào)整WPE的一些設(shè)定值。

                FILTER的詳細(xì)教學(xué)

                - 當(dāng)FILTER在啟動狀態(tài)時 ,ON的按鈕會呈現(xiàn)紅色。- 當(dāng)您啟動FILTER時,您隨時可以關(guān)閉這個視窗。FILTER將會留在原來的狀態(tài),直到您再按一次 on / off 鈕。- 只有FILTER啟用鈕在OFF的狀態(tài)下,才可以勾選Filter前的方框來編輯修改。- 當(dāng)您想編輯某個Filter,只要雙擊該Filter的名字即可。

                NORMAL MODE:

                范例:

                當(dāng)您在 Street Fighter Online ﹝快打旋風(fēng)線上版﹞游戲中,您使用了兩次火球而且擊中了對方,這時您會擷取到以下的封包: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的值出現(xiàn),應(yīng)該就是這里了。

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

                因此您將0A 09 C1 10填在搜尋列﹝SEARCH﹞,然后在修改列﹝MODIFY﹞的位置4填上FF.如此一來,當(dāng)您再度發(fā)出火球時,F(xiàn)F會取代之前的10,也就是攻擊力為255的火球了!

                ADVANCED MODE:范例: 當(dāng)您在一個游戲中,您不想要用真實(shí)姓名,您想用修改過的假名傳送給對方。在您使用TRACE后,您會發(fā)現(xiàn)有些封包里面有您的名字出現(xiàn)。假設(shè)您的名字是 Shadow,換算成16進(jìn)位則是﹝53 68 61 64 6F 77﹞;而您打算用moon﹝6D6F 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但是您仔細(xì)看,您的名字在每個封包中并不是出現(xiàn)在相同的位置上- 在第2個封包里,名字是出現(xiàn)在第4個位置上- 在第4個封包里,名字是出現(xiàn)在第6個位置上在這種情況下,您就需要使用ADVANCED MODE- 您在搜尋列﹝SEARCH﹞填上:53 68 61 64 6F 77 ﹝請務(wù)必從位置1開始填﹞- 您想要從原來名字Shadow的第一個字母開始置換新名字,因此您要選擇從數(shù)值被發(fā)現(xiàn)的位置開始替代連續(xù)數(shù)值﹝from the position of the chain found﹞.- 現(xiàn)在,在修改列﹝MODIFY﹞000的位置填上:6D 6F 6F 6E 20 20 ﹝此為相對應(yīng)位置,也就是從原來搜尋欄的+001位置開始遞換﹞- 如果您想從封包的第一個位置就修改數(shù)值,請選擇﹝from the beginning of the packet﹞了解一點(diǎn)TCP/IP協(xié)議常識的人都知道,互聯(lián)網(wǎng)是 將信息數(shù)據(jù)打包之后再傳送出去的。每個數(shù)據(jù)包分為頭部信息和數(shù)據(jù)信息兩部分。頭部信息包括數(shù)據(jù)包的發(fā)送地址和到達(dá)地址等。數(shù)據(jù)信息包括我們在游戲中相關(guān)操 作的各項信息。那么在做截獲封包的過程之前我們先要知道游戲服務(wù)器的IP地址和端口號等各種信息,實(shí)際上最簡單的是看看我們游戲目錄下,是否有一個 SERVER.INI的配置文件,這個文件里你可以查看到個游戲服務(wù)器的IP地址,比如金庸群俠傳就是如此,那么除了這個我們還可以在DOS下使用 NETSTAT這個命令, NETSTAT命令的功能是顯示網(wǎng)絡(luò)連接、路由表和網(wǎng)絡(luò)接口信息,可以讓用戶得知目前都有哪些網(wǎng)絡(luò)連接正在運(yùn)作。或者你可以使用木馬客星等工具來查看網(wǎng)絡(luò) 連接。工具是很多的,看你喜歡用哪一種了。

                NETSTAT命令的一般格式為:NETSTAT [選項]命令中各選項的含義如下:-a 顯示所有socket,包括正在監(jiān)聽的。-c 每隔1秒就重新顯示一遍,直到用戶中斷它。

                -i 顯示所有網(wǎng)絡(luò)接口的信息。-n 以網(wǎng)絡(luò)IP地址代替名稱,顯示出網(wǎng)絡(luò)連接情形。-r 顯示核心路由表,格式同"route -e".-t 顯示TCP協(xié)議的連接情況。-u 顯示UDP協(xié)議的連接情況。-v 顯示正在進(jìn)行的工作。

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

                首先我們將WPE截獲的封包保存為文本文件,然后打開它,這時會看到如下的數(shù)據(jù)(這里我們以金庸群俠傳里PK店小二客戶端發(fā)送的數(shù)據(jù)為例來講解):第一個 文件: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我們發(fā)現(xiàn)兩次PK店小二的數(shù)據(jù)格式一樣,但是內(nèi)容卻不相同,我們是PK的同一個NPC,為什么會不同呢? 原來金庸群俠傳的封包是經(jīng)過了加密運(yùn)算才在網(wǎng)路上傳輸?shù)模敲次覀兠媾R的問題就是如何將密文解密成明文再分析了。

                因為一般的數(shù)據(jù)包加密都是異或運(yùn)算,所以這里先講一下什么是異或。 簡單的說,異或就是"相同為0,不同為1"(這是針對二進(jìn)制按位來講的),舉個例子,0001和0010異或,我們按位對比,得到異或結(jié)果是0011,計 算的方法是:0001的第4位為0,0010的第4位為0,它們相同,則異或結(jié)果的第4位按照"相同為0,不同為1"的原則得到0,0001的第3位為 0,0010的第3位為0,則異或結(jié)果的第3位得到0,0001的第2位為0,0010的第2位為1,則異或結(jié)果的第2位得到1,0001的第1位為 1,0010的第1位為0,則異或結(jié)果的第1位得到1,組合起來就是0011.異或運(yùn)算今后會遇到很多,大家可以先熟悉熟悉,熟練了對分析很有幫助的。

                下面我們繼續(xù)看看上面的兩個文件,按照常理,數(shù)據(jù)包的數(shù)據(jù)不會全部都有值的,游戲開發(fā)時會預(yù)留一些字節(jié)空間來便于日后的擴(kuò)充,也就是說數(shù)據(jù)包里會存在一些"00"的字節(jié),觀察上面的文件,我們會發(fā)現(xiàn)文件一里很多"12",文件二里很多"77",那么這是不是代表我們說的"00"呢?推理到這里,我們就開始行動吧!

                我們把文件一與"12"異或,文件二與"77"異或,當(dāng)然用手算很費(fèi)事,我們使用"M2M 1.0 加密封包分析工具"來計算就方便多了。得到下面的結(jié)果:第一個文件: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哈,這一下兩個文件大部分都一樣啦,說明我們的推理是正確的,上面就是我們需要的明文!

                接下來就是搞清楚一些關(guān)鍵的字節(jié)所代表的含義,這就需要截獲大量的數(shù)據(jù)來分析。

                首先我們會發(fā)現(xiàn)每個數(shù)據(jù)包都是"F4 44"開頭,第3個字節(jié)是變化的,但是變化很有規(guī)律。我們來看看各個包的長度,發(fā)現(xiàn)什么沒有?對了,第3個字節(jié)就是包的長度! 通過截獲大量的數(shù)據(jù)包,我們判斷第4個字節(jié)代表指令,也就是說客戶端告訴服務(wù)器進(jìn)行的是什么操作。例如向服務(wù)器請求戰(zhàn)斗指令為"30",戰(zhàn)斗中移動指令 為"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",在這個包里包含什么信息呢?應(yīng)該有通知服務(wù)器你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 我們根據(jù)常理分析,游戲里的NPC種類雖然不會超過65535(FFFF),但開發(fā)時不會把自己限制在字的范圍,那樣不利于游戲的擴(kuò)充,所以我們在雙字里 看看。通過"店小二"和"小嘍羅"兩個包的對比,我們把目標(biāo)放在"6C 79 F6 05"和"CF 26 00 00"上。(對比一下很容易的,但你不能太遲鈍咯,呵呵)我們再看看后面的包,在后面的包里應(yīng)該還會出現(xiàn)NPC的代碼,比如移動的包,游戲允許觀戰(zhàn),服務(wù) 器必然需要知道NPC的移動坐標(biāo),再廣播給觀戰(zhàn)的其他玩家。在后面第4個包"SEND-> 0000 F4 44 10 DA 01 DB 6C 79 F6 05 02 27 35 01 00 00"里我們又看到了"6C 79 F6 05",初步斷定店小二的代碼就是它了!(這分析里邊包含了很多工作的,大家可以用WPE截下數(shù)據(jù)來自己分析分析)

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

                我們看看第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根據(jù)上面的分析,黃狗的代碼為"4B 7D F6 05"(100040011),不過兩只黃狗服務(wù)器怎樣分辨呢?看看"EB 03 F8 05"(100140011),是上一個代碼加上100000,呵呵,這樣服務(wù)器就可以認(rèn)出兩只黃狗了。我們再通過野外遇敵截獲的數(shù)據(jù)包來證實(shí),果然如 此。

                那么,這個包的格式應(yīng)該比較清楚了:第3個字節(jié)為包的長度,"DA"為指令,第5個字節(jié)為NPC個數(shù),從第7個字節(jié)開始的10個字節(jié)代表一個NPC的信息,多一個NPC就多10個字節(jié)來表示。

                大家如果玩過網(wǎng)金,必然知道隨機(jī)遇敵有時會出現(xiàn)增援,我們就利用游戲這個增援來讓每次戰(zhàn)斗都會出現(xiàn)增援的NPC吧。

                通過在戰(zhàn)斗中出現(xiàn)增援截獲的數(shù)據(jù)包,我們會發(fā)現(xiàn)服務(wù)器端發(fā)送了這樣一個包:F4 44 12 E9 EB 03 F8 05 02 00 00 03 00 00 00 00 00 00 第5-第8個字節(jié)為增援NPC的代碼(這里我們就簡單的以黃狗的代碼來舉例)。 那么,我們就利用單機(jī)代理技術(shù)來同時欺騙客戶端和服務(wù)器吧!

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

                四:怎么冒充"客戶端"向"服務(wù)器"發(fā)我們需要的封包?

                這里我們需要使用一個工具,它位于客戶端和服務(wù)器端之間,它的工作就是進(jìn)行數(shù)據(jù)包的接收和轉(zhuǎn)發(fā),這個工具我們稱為代理。如果代理的工作單純就是接收和轉(zhuǎn)發(fā) 的話,這就毫無意義了,但是請注意:所有的數(shù)據(jù)包都要通過它來傳輸,這里的意義就重大了。我們可以分析接收到的數(shù)據(jù)包,或者直接轉(zhuǎn)發(fā),或者修改后轉(zhuǎn)發(fā),或 者壓住不轉(zhuǎn)發(fā),甚至偽造我們需要的封包來發(fā)送。

                下面我們繼續(xù)講怎樣來同時欺騙服務(wù)器和客戶端,也就是修改封包和偽造封包。 通過我們上節(jié)的分析,我們已經(jīng)知道了打多個NPC的封包格式,那么我們就動手吧!

                首先我們要查找客戶端發(fā)送的包,找到戰(zhàn)斗的特征,就是請求戰(zhàn)斗的第1個包,我們找"F4 44 1F 30"這個特征,這是不會改變的,當(dāng)然是要解密后來查找哦。 找到后,表示客戶端在向服務(wù)器請求戰(zhàn)斗,我們不動這個包,轉(zhuǎn)發(fā)。 繼續(xù)向下查找,這時需要查找的特征碼不太好辦,我們先查找"DA",這是客戶端發(fā)送NPC信息的數(shù)據(jù)包的指令,那么可能其他包也有"DA",沒關(guān)系,我們 看前3個字節(jié)有沒有"F4 44"就行了。找到后,我們的工作就開始了!

                我們確定要打的NPC數(shù)量。這個數(shù)量不能很大,原因在于網(wǎng)金的封包長度用一個字節(jié)表示,那么一個包可以有255個字節(jié),我們上面分析過,增加一個NPC要增加10個字節(jié),所以大家算算就知道,打20個NPC比較合適。

                然后我們要把客戶端原來的NPC代碼分析計算出來,因為增加的NPC代碼要加上100000哦。再把我們增加的NPC代碼計算出來,并且組合成新的封包,注意代表包長度的字節(jié)要修改啊,然后轉(zhuǎn)發(fā)到服務(wù)器,這一步在編寫程序的時候要注意算法,不要造成較大延遲。

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

                發(fā)送了上面的封包后,我們根據(jù)新增NPC代碼構(gòu)造封包馬上發(fā)給客戶端,格式就是"F4 44 12 E9 NPC代碼 02 00 00 03 00 00 00 00 00 00",把每個新增的NPC都構(gòu)造這樣一個包,按順序連在一起發(fā)送給客戶端,客戶端也就被我們騙過了,很簡單吧。

                以后戰(zhàn)斗中其他的事我們就不管了,盡情地開打吧。

                游戲外掛基本原理及實(shí)現(xiàn)

                解釋游戲外掛的基本原理和實(shí)現(xiàn)方法

                游戲外掛已經(jīng)深深地影響著眾多網(wǎng)絡(luò)游戲玩家,今天在網(wǎng)上看到了一些關(guān)于游戲外掛編寫的技術(shù),于是轉(zhuǎn)載上供大家參考

                1、游戲外掛的原理

                外掛現(xiàn)在分為好多種,比如模擬鍵盤的,鼠標(biāo)的,修改數(shù)據(jù)包的,還有修改本地內(nèi)存的,但好像沒有修改服務(wù)器內(nèi)存的哦,呵呵。其實(shí)修改服務(wù)器也是有辦法的,只是技術(shù)太高一般人沒有辦法入手而已。(比如請GM去夜總會、送禮、收黑錢等等辦法都可以修改服務(wù)器數(shù)據(jù),哈哈)

                修改游戲無非是修改一下本地內(nèi)存的數(shù)據(jù),或者截獲API函數(shù)等等。這里我把所能想到的方法都作一個介紹,希望大家能做出很好的外掛來使游戲廠商更好的完善 自己的技術(shù)。我見到一篇文章是講魔力寶貝的理論分析,寫得不錯,大概是那個樣子。下來我就講解一下技術(shù)方面的東西,以作引玉之用。


               2 技術(shù)分析部分

                2.1 模擬鍵盤或鼠標(biāo)的響應(yīng)

                我們一般使用:

            UINT SendInput(
                UINT nInputs,   // count of input events
               PINPUT pInputs, // array of input events
                int cbSize    // size of structure
              );
                API函數(shù)。第一個參數(shù)是說明第二個參數(shù)的矩陣的維數(shù)的,第二個參數(shù)包含了響應(yīng)事件,這個自己填充就可以,最后是這個結(jié)構(gòu)的大小,非常簡單,這是最簡單的方法模擬鍵盤鼠標(biāo)了,呵呵。注意,這個函數(shù)還有個替代函數(shù):

            VOID keybd_event(
                BYTE bVk,       // 虛擬鍵碼
                BYTE bScan,      // 掃描碼
                DWORD dwFlags,
                ULONG_PTR dwExtraInfo // 附加鍵狀態(tài)
              );
              與
              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
              );
                這兩個函數(shù)非常簡單了,我想那些按鍵精靈就是用的這個吧。上面的是模擬鍵盤,下面的是模擬鼠標(biāo)的。這個僅僅是模擬部分,要和游戲聯(lián)系起來我們還需要找到游 戲的窗口才行,或者包含快捷鍵,就象按鍵精靈的那個激活鍵一樣,我們可以用GetWindow函數(shù)來枚舉窗口,也可以用Findwindow函數(shù)來查找制 定的窗口(注意,還有一個FindWindowEx),F(xiàn)indwindowEx可以找到窗口的子窗口,比如按鈕,等什么東西。當(dāng)游戲切換場景的時候我們 可以用FindWindowEx來確定一些當(dāng)前窗口的特征,從而判斷是否還在這個場景,方法很多了,比如可以GetWindowInfo來確定一些東西, 比如當(dāng)查找不到某個按鈕的時候就說明游戲場景已經(jīng)切換了,等等辦法。有的游戲沒有控件在里面,這是對圖像做坐標(biāo)變換的話,這種方法就要受到限制了。這就需 要我們用別的辦法來輔助分析了。

                至于快捷鍵我們要用動態(tài)連接庫實(shí)現(xiàn)了,里面要用到hook技術(shù)了,這個也非常簡單。大家可能都會了,其實(shí)就是一個全局的hook對象然后 SetWindowHook就可以了,回調(diào)函數(shù)都是現(xiàn)成的,而且現(xiàn)在網(wǎng)上的例子多如牛毛。這個實(shí)現(xiàn)在外掛中已經(jīng)很普遍了。如果還有誰不明白,那就去看看 MSDN查找SetWindowHook就可以了。

                不要低估了這個動態(tài)連接庫的作用,它可以切入所有的進(jìn)程空間,也就是可以加載到所有的游戲里面哦,只要用對,你會發(fā)現(xiàn)很有用途的。這個需要你復(fù)習(xí)一下Win32編程的基礎(chǔ)知識了。呵呵,趕快去看書吧。

                2.2 截獲消息

                有些游戲的響應(yīng)機(jī)制比較簡單,是基于消息的,或者用什么定時器的東西。這個時候你就可以用攔截消息來實(shí)現(xiàn)一些有趣的功能了。

                我們攔截消息使用的也是hook技術(shù),里面包括了鍵盤消息,鼠標(biāo)消息,系統(tǒng)消息,日志等,別的對我們沒有什么大的用處,我們只用攔截消息的回調(diào)函數(shù)就可以 了,這個不會讓我寫例子吧。其實(shí)這個和上面的一樣,都是用SetWindowHook來寫的,看看就明白了很簡單的。

                至于攔截了以后做什么就是你的事情了,比如在每個定時器消息里面處理一些我們的數(shù)據(jù)判斷,或者在定時器里面在模擬一次定時器,那么有些數(shù)據(jù)就會處理兩次, 呵呵。后果嘛,不一定是好事情哦,呵呵,不過如果數(shù)據(jù)計算放在客戶端的游戲就可以真的改變數(shù)據(jù)了,呵呵,試試看吧。用途還有很多,自己想也可以想出來的, 呵呵。

                2.3 攔截Socket包

                這個技術(shù)難度要比原來的高很多。

                首先我們要替換WinSock.DLL或者WinSock32.DLL,我們寫的替換函數(shù)要和原來的函數(shù)一致才行,就是說它的函數(shù)輸出什么樣的,我們也要 輸出什么樣子的函數(shù),而且參數(shù),參數(shù)順序都要一樣才行,然后在我們的函數(shù)里面調(diào)用真正的WinSock32.DLL里面的函數(shù)就可以了。

                首先:我們可以替換動態(tài)庫到系統(tǒng)路徑。

                其次:我們應(yīng)用程序啟動的時候可以加載原有的動態(tài)庫,用這個函數(shù)LoadLibary然后定位函數(shù)入口用GetProcAddress函數(shù)獲得每個真正Socket函數(shù)的入口地址。

                當(dāng)游戲進(jìn)行的時候它會調(diào)用我們的動態(tài)庫,然后從我們的動態(tài)庫中處理完畢后才跳轉(zhuǎn)到真正動態(tài)庫的函數(shù)地址,這樣我們就可以在里面處理自己的數(shù)據(jù)了,應(yīng)該是一 切數(shù)據(jù)。呵呵,興奮吧,攔截了數(shù)據(jù)包我們還要分析之后才能進(jìn)行正確的應(yīng)答,不要以為這樣工作就完成了,還早呢。等分析完畢以后我們還要仿真應(yīng)答機(jī)制來和服 務(wù)器通信,一個不小心就會被封號。

                分析數(shù)據(jù)才是工作量的來源呢,游戲每次升級有可能加密方式會有所改變,因此我們寫外掛的人都是亡命之徒啊,被人愚弄了還不知道。

                2.4 截獲API

                上面的技術(shù)如果可以靈活運(yùn)用的話我們就不用截獲API函數(shù)了,其實(shí)這種技術(shù)是一種補(bǔ)充技術(shù)。比如我們需要截獲Socket以外的函數(shù)作為我們的用途,我們就要用這個技術(shù)了,其實(shí)我們也可以用它直接攔截在Socket中的函數(shù),這樣更直接。

                現(xiàn)在攔截API的教程到處都是,我就不列舉了,我用的比較習(xí)慣的方法是根據(jù)輸入節(jié)進(jìn)行攔截的,這個方法可以用到任何一種操作系統(tǒng)上,比如Windows 98/2000等,有些方法不是跨平臺的,我不建議使用。這個技術(shù)大家可以參考《Windows核心編程》里面的545頁開始的內(nèi)容來學(xué)習(xí),如果是 Win98系統(tǒng)可以用“Windows系統(tǒng)奧秘”那個最后一章來學(xué)習(xí)。

                網(wǎng)絡(luò)游戲通訊模型初探

                [文章導(dǎo)讀]本文就將圍繞三個主題來給大家講述一下網(wǎng)絡(luò)游戲的網(wǎng)絡(luò)互連實(shí)現(xiàn)方法

                序言

                網(wǎng)絡(luò)游戲,作為游戲與網(wǎng)絡(luò)有機(jī)結(jié)合的產(chǎn)物,把玩家?guī)肓诵碌膴蕵奉I(lǐng)域。網(wǎng)絡(luò)游戲在中國開始發(fā)展至今也僅有3,4年的歷史,跟已經(jīng)擁有幾十年開發(fā)歷史的單機(jī)游戲相比,網(wǎng)絡(luò)游戲還是非常年輕的。當(dāng)然,它的形成也是根據(jù)歷史變化而產(chǎn)生的可以說沒有互聯(lián)網(wǎng)的 興起,也就沒有網(wǎng)絡(luò)游戲的誕生。作為新興產(chǎn)物,網(wǎng)絡(luò)游戲的開發(fā)對廣大開發(fā)者來說更加神秘,對于一個未知領(lǐng)域,開發(fā)者可能更需要了解的是網(wǎng)絡(luò)游戲與普通單機(jī) 游戲有何區(qū)別,網(wǎng)絡(luò)游戲如何將玩家們連接起來,以及如何為玩家提供一個互動的娛樂環(huán)境。本文就將圍繞這三個主題來給大家講述一下網(wǎng)絡(luò)游戲的網(wǎng)絡(luò)互連實(shí)現(xiàn)方 法。

                網(wǎng)絡(luò)游戲與單機(jī)游戲

                說到網(wǎng)絡(luò)游戲,不得不讓人聯(lián)想到單機(jī)游戲,實(shí)際上網(wǎng)絡(luò)游戲的實(shí)質(zhì)脫離不了單機(jī)游戲的制作思想,網(wǎng)絡(luò)游戲和單機(jī)游戲的差別大家可以很直接的想到:不就是可以 多人連線嗎?沒錯,但如何實(shí)現(xiàn)這些功能,如何把網(wǎng)絡(luò)連線合理的融合進(jìn)單機(jī)游戲,就是我們下面要討論的內(nèi)容。在了解網(wǎng)絡(luò)互連具體實(shí)現(xiàn)之前,我們先來了解一下 單機(jī)與網(wǎng)絡(luò)它們各自的運(yùn)行流程,只有了解這些,你才能深入網(wǎng)絡(luò)游戲開發(fā)的核心。

                現(xiàn)在先讓我們來看一下普通單機(jī)游戲的簡化執(zhí)行流程:

            Initialize() // 初始化模塊
            {
             初始化游戲數(shù)據(jù);
            }
            Game() // 游戲循環(huán)部分
            {
             繪制游戲場景、人物以及其它元素;
             獲取用戶操作輸入;
             switch( 用戶輸入數(shù)據(jù))
             {
              case 移動:
              {
               處理人物移動;
              }
              break;
              case 攻擊:
              {
               處理攻擊邏輯:
              }
              break;
              ...
              其它處理響應(yīng);
              ...
              default:
               break;
             }
             游戲的NPC等邏輯AI處理;
            }
            Exit() // 游戲結(jié)束
            {
             釋放游戲數(shù)據(jù);
             離開游戲;
            }

                我們來說明一下上面單機(jī)游戲的流程。首先,不管是游戲軟件還是其他應(yīng)用軟件,初始化部分必不可少,這里需要對游戲的數(shù)據(jù)進(jìn)行初始化,包括圖像、聲音以及一 些必備的數(shù)據(jù)。接下來,我們的游戲?qū)鼍啊⑷宋镆约捌渌剡M(jìn)行循環(huán)繪制,把游戲世界展現(xiàn)給玩家,同時接收玩家的輸入操作,并根據(jù)操作來做出響應(yīng),此外, 游戲還需要對NPC以及一些邏輯AI進(jìn)行處理。最后,游戲數(shù)據(jù)被釋放,游戲結(jié)束。

                網(wǎng)絡(luò)游戲與單機(jī)游戲有一個很顯著的差別,就是網(wǎng)絡(luò)游戲除了一個供操作游戲的用戶界面平臺(如單機(jī)游戲)外,還需要一個用于連接所有用戶,并為所有用戶提供數(shù)據(jù)服務(wù)的服務(wù)器,從某些角度來看,游戲服務(wù)器就像一個大型的數(shù)據(jù)庫,

                提供數(shù)據(jù)以及數(shù)據(jù)邏輯交互的功能。讓我們來看看一個簡單的網(wǎng)絡(luò)游戲模型執(zhí)行流程:

                客戶機(jī):

            Login()// 登入模塊
            {
             初始化游戲數(shù)據(jù);
             獲取用戶輸入的用戶和密碼;
             與服務(wù)器創(chuàng)建網(wǎng)絡(luò)連接;
             發(fā)送至服務(wù)器進(jìn)行用戶驗證;
             ...
             等待服務(wù)器確認(rèn)消息;
             ...
             獲得服務(wù)器反饋的登入消息;
             if( 成立 )
              進(jìn)入游戲;
             else
              提示用戶登入錯誤并重新接受用戶登入;
            }
            Game()// 游戲循環(huán)部分
            {
             繪制游戲場景、人物以及其它元素;
             獲取用戶操作輸入;
             將用戶的操作發(fā)送至服務(wù)器;
             ...
             等待服務(wù)器的消息;
             ...
             接收服務(wù)器的反饋信息;
             switch( 服務(wù)器反饋的消息數(shù)據(jù) )
             {
              case 本地玩家移動的消息:
              {
               if( 允許本地玩家移動 )
                客戶機(jī)處理人物移動;
               else
                客戶機(jī)保持原有狀態(tài);
              }
               break;
              case 其他玩家/NPC的移動消息:
              {
               根據(jù)服務(wù)器的反饋信息進(jìn)行其他玩家或者NPC的移動處理;
              }
              break;
              case 新玩家加入游戲:
              {
               在客戶機(jī)中添加顯示此玩家;
              }
               break;
              case 玩家離開游戲:
              {
               在客戶機(jī)中銷毀此玩家數(shù)據(jù);
              }
               break;
              ...
              其它消息類型處理;
              ... 
              default:
               break;
             }
            }
            Exit()// 游戲結(jié)束
            {
             發(fā)送離開消息給服務(wù)器;
             ...
             等待服務(wù)器確認(rèn);
             ...
             得到服務(wù)器確認(rèn)消息;
             與服務(wù)器斷開連接;
             釋放游戲數(shù)據(jù);
             離開游戲;
            }


              服務(wù)器:

            Listen()  // 游戲服務(wù)器等待玩家連接模塊
            {
             ...
             等待用戶的登入信息;
             ...
             接收到用戶登入信息;
             分析用戶名和密碼是否符合;
             if( 符合 )
             {
              發(fā)送確認(rèn)允許進(jìn)入游戲消息給客戶機(jī); 
              把此玩家進(jìn)入游戲的消息發(fā)布給場景中所有玩家;
              把此玩家添加到服務(wù)器場景中;
             }
             else
             {
              斷開與客戶機(jī)的連接;
             }
            }
            Game() // 游戲服務(wù)器循環(huán)部分
            {
             ...
             等待場景中玩家的操作輸入;
             ...
             接收到某玩家的移動輸入或NPC的移動邏輯輸入;
             // 此處只以移動為例
             進(jìn)行此玩家/NPC在地圖場景是否可移動的邏輯判斷;

             if( 可移動 )
             {
              對此玩家/NPC進(jìn)行服務(wù)器移動處理;
              發(fā)送移動消息給客戶機(jī);
              發(fā)送此玩家的移動消息給場景上所有玩家;
             }
             else
              發(fā)送不可移動消息給客戶機(jī);
            }
            Exit()  // 游戲服務(wù)=器結(jié)束
            {
             接收到玩家離開消息;
             將此消息發(fā)送給場景中所有玩家;
             發(fā)送允許離開的信息;
             將玩家數(shù)據(jù)存入數(shù)據(jù)庫;
             注銷此玩家在服務(wù)器內(nèi)存中的數(shù)據(jù);
            }
            }


                讓我們來說明一下上面簡單網(wǎng)絡(luò)游戲模型的運(yùn)行機(jī)制。先來講講服務(wù)器端,這里服務(wù)器端分為三個部分(實(shí)際上一個完整的網(wǎng)絡(luò)游戲遠(yuǎn)不止這些):登入模塊、游戲 模塊和登出模塊。登入模塊用于監(jiān)聽網(wǎng)絡(luò)游戲客戶端發(fā)送過來的網(wǎng)絡(luò)連接消息,并且驗證其合法性,然后在服務(wù)器中創(chuàng)建這個玩家并且把玩家?guī)ьI(lǐng)到游戲模塊中; 游戲模塊則提供給玩家用戶實(shí)際的應(yīng)用服務(wù),我們在后面會詳細(xì)介紹這個部分; 在得到玩家要離開游戲的消息后,登出模塊則會把玩家從服務(wù)器中刪除,并且把玩家的屬性數(shù)據(jù)保存到服務(wù)器數(shù)據(jù)庫中,如: 經(jīng)驗值、等級、生命值等。

                接下來讓我們看看網(wǎng)絡(luò)游戲的客戶端。這時候,客戶端不再像單機(jī)游戲一樣,初始化數(shù)據(jù)后直接進(jìn)入游戲,而是在與服務(wù)器創(chuàng)建連接,并且獲得許可的前提下才進(jìn)入 游戲。除此之外,網(wǎng)絡(luò)游戲的客戶端游戲進(jìn)程需要不斷與服務(wù)器進(jìn)行通訊,通過與服務(wù)器交換數(shù)據(jù)來確定當(dāng)前游戲的狀態(tài),例如其他玩家的位置變化、物品掉落情 況。同樣,在離開游戲時,客戶端會向服務(wù)器告知此玩家用戶離開,以便于服務(wù)器做出相應(yīng)處理。

                以上用簡單的偽代碼給大家闡述了單機(jī)游戲與網(wǎng)絡(luò)游戲的執(zhí)行流程,大家應(yīng)該可以清楚看出兩者的差別,以及兩者間相互的關(guān)系。我們可以換個角度考慮,網(wǎng)絡(luò)游戲 就是把單機(jī)游戲的邏輯運(yùn)算部分搬移到游戲服務(wù)器中進(jìn)行處理,然后把處理結(jié)果(包括其他玩家數(shù)據(jù))通過游戲服務(wù)器返回給連接的玩家。

                網(wǎng)絡(luò)互連

                在了解了網(wǎng)絡(luò)游戲基本形態(tài)之后,讓我們進(jìn)入真正的實(shí)際應(yīng)用部分。首先,作為網(wǎng)絡(luò)游戲,除了常規(guī)的單機(jī)游戲所必需的東西之外,我們還需要增加一個網(wǎng)絡(luò)通訊模塊,當(dāng)然,這也是網(wǎng)絡(luò)游戲較為主要的部分,我們來討論一下如何實(shí)現(xiàn)網(wǎng)絡(luò)的通訊模塊。

                一個完善的網(wǎng)絡(luò)通訊模塊涉及面相當(dāng)廣,本文僅對較為基本的處理方式進(jìn)行討論。網(wǎng)絡(luò)游戲是由客戶端和服務(wù)器組成,相應(yīng)也需要兩種不同的網(wǎng)絡(luò)通訊處理方式,不 過也有相同之處,我們先就它們的共同點(diǎn)來進(jìn)行介紹。我們這里以Microsoft Windows 2000 [2000 Server]作為開發(fā)平臺,并且使用Winsock作為網(wǎng)絡(luò)接口(可能一些朋友會考慮使用DirectPlay來進(jìn)行網(wǎng)絡(luò)通訊,不過對于當(dāng)前在線游 戲,DirectPlay并不適合,具體原因這里就不做討論了)。

                確定好平臺與接口后,我們開始進(jìn)行網(wǎng)絡(luò)連接創(chuàng)建之前的一些必要的初始化工作,這部分無論是客戶端或者服務(wù)器都需要進(jìn)行。讓我們看看下面的代碼片段:

            WORD wVersionRequested;
            WSADATAwsaData;
            wVersionRequested MAKEWORD(1, 1);
            if( WSAStartup( wVersionRequested, &wsaData ) !0 )
            {
             Failed( WinSock Version Error!" );
            }
              上面通過調(diào)用Windows的socket API函數(shù)來初始化網(wǎng)絡(luò)設(shè)備,接下來進(jìn)行網(wǎng)絡(luò)Socket的創(chuàng)建,代碼片段如下:

            SOCKET sSocket socket( AF_INET, m_lProtocol, 0 );
            if( sSocket == INVALID_SOCKET )
            {
             Failed( "WinSocket Create Error!" );
            }

                這里需要說明,客戶端和服務(wù)端所需要的Socket連接數(shù)量是不同的,客戶端只需要一個Socket連接足以滿足游戲的需要,而服務(wù)端必須為每個玩家用戶 創(chuàng)建一個用于通訊的Socket連接。當(dāng)然,并不是說如果服務(wù)器上沒有玩家那就不需要創(chuàng)建Socket連接,服務(wù)器端在啟動之時會生成一個特殊的 Socket用來對玩家創(chuàng)建與服務(wù)器連接的請求進(jìn)行響應(yīng),等介紹網(wǎng)絡(luò)監(jiān)聽部分后會有更詳細(xì)說明。

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

            if( sSocket != INVALID_SOCKET )
            {
             closesocket( sSocket );
            }
            if( WSACleanup() != 0 )
            {
             Warning( "Can't release Winsocket" );
            }

                這里兩個步驟分別對前面所作的創(chuàng)建初始化進(jìn)行了相應(yīng)釋放。

                接下來看看服務(wù)器端的一個網(wǎng)絡(luò)執(zhí)行處理,這里我們假設(shè)服務(wù)器端已經(jīng)創(chuàng)建好一個Socket供使用,我們要做的就是讓這個Socket變成監(jiān)聽網(wǎng)絡(luò)連接請求的專用接口,看看下面代碼片段:

            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為要監(jiān)聽的端口號
            // 綁定socket
            if( bind( sSocket, (SOCKADDR*)&addr, sizeof(addr) ) == SOCKET_ERROR )
            {
             Failed( "WinSocket Bind Error!");
            }
            // 進(jìn)行監(jiān)聽
            if( listen( sSocket, SOMAXCONN ) == SOCKET_ERROR )
            {
             Failed( "WinSocket Listen Error!");
            }

                這里使用的是阻塞式通訊處理,此時程序?qū)⑻幱诘却婕矣脩暨B接的狀態(tài),倘若這時候有客戶端連接進(jìn)來,則通過accept()來創(chuàng)建針對此玩家用戶的Socket連接,代碼片段如下:

            sockaddraddrServer;
            int nLen sizeof( addrServer );
            SOCKET sPlayerSocket accept( sSocket, &addrServer, &nLen );
            if( sPlayerSocket == INVALID_SOCKET )
            {
             Failed( WinSocket Accept Error!");
            }

                這里我們創(chuàng)建了sPlayerSocket連接,此后游戲服務(wù)器與這個玩家用戶的通訊全部通過此Socket進(jìn)行,到這里為止,我們服務(wù)器已經(jīng)有了接受玩家用戶連接的功能,現(xiàn)在讓我們來看看游戲客戶端是如何連接到游戲服務(wù)器上,代碼片段如下:

            SOCKADDR_IN addr;
            memset( &addr, 0, sizeof(addr) );
            addr.sin_family = AF_INET;// 要連接的游戲服務(wù)器端口號
            addr.sin_addr.s_addr = inet_addr( IP );// 要連接的游戲服務(wù)器IP地址,
            addr.sin_port = htons( Port );//到此,客戶端和服務(wù)器已經(jīng)有了通訊的橋梁,
            //接下來就是進(jìn)行數(shù)據(jù)的發(fā)送和接收:
            connect( sSocket, (SOCKADDR*)&addr, sizeof(addr) );
            if( send( sSocket, pBuffer, lLength, 0 ) == SOCKET_ERROR )
            {
             Failed( "WinSocket Send Error!");
            }


                這里的pBuffer為要發(fā)送的數(shù)據(jù)緩沖指針,lLength為需要發(fā)送的數(shù)據(jù)長度,通過這支Socket API函數(shù),我們無論在客戶端或者服務(wù)端都可以進(jìn)行數(shù)據(jù)的發(fā)送工作,同時,我們可以通過recv()這支Socket API函數(shù)來進(jìn)行數(shù)據(jù)接收:

            lLength, 0 ) == SOCKET_ERROR )
            {
             Failed( "WinSocket Recv Error!");
            }

                其中pBuffer用來存儲獲取的網(wǎng)絡(luò)數(shù)據(jù)緩沖,lLength則為需要獲取的數(shù)據(jù)長度。


                現(xiàn)在,我們已經(jīng)了解了一些網(wǎng)絡(luò)互連的基本知識,但作為網(wǎng)絡(luò)游戲,如此簡單的連接方式是無法滿足網(wǎng)絡(luò)游戲中百人千人同時在線的,我們需要更合理容錯性更強(qiáng)的網(wǎng)絡(luò)通訊處理方式,當(dāng)然,我們需要先了解一下網(wǎng)絡(luò)游戲?qū)W(wǎng)絡(luò)通訊的需求是怎樣的。

                大家知道,游戲需要不斷循環(huán)處理游戲中的邏輯并進(jìn)行游戲世界的繪制,上面所介紹的Winsock處理方式均是以阻塞方式進(jìn)行,這樣就違背了游戲的執(zhí)行本 質(zhì),可以想象,在客戶端連接到服務(wù)器的過程中,你的游戲不能得到控制,這時如果玩家想取消連接或者做其他處理,甚至顯示一個最基本的動態(tài)連接提示都不行。

                所以我們需要用其他方式來處理網(wǎng)絡(luò)通訊,使其不會與游戲主線相沖突,可能大家都會想到: 創(chuàng)建一個網(wǎng)絡(luò)線程來處理不就可以了?沒錯,我們可以創(chuàng)建一個專門用于網(wǎng)絡(luò)通訊的子線程來解決這個問題。當(dāng)然,我們游戲中多了一個線程,我們就需要做更多的 考慮,讓我們來看看如何創(chuàng)建網(wǎng)絡(luò)通訊線程。

                在Windows系統(tǒng)中,我們可以通過CreateThread()函數(shù)來進(jìn)行線程的創(chuàng)建,看看下面的代碼片段:

            DWORD dwThreadID;
            HANDLE hThread = CreateThread( NULL, 0, NetThread/*網(wǎng)絡(luò)線程函式*/, sSocket, 0, &dwThreadID );
            if( hThread == NULL )
            {
             Failed( "WinSocket Thread Create Error!");
            }
              這里我們創(chuàng)建了一個線程,同時將我們的Socket傳入線程函數(shù):

            DWORD WINAPINetThread(LPVOID lParam)

            {
             SOCKET sSocket (SOCKET)lParam;
             ...
             return 0;
            }

                NetThread就是我們將來用于處理網(wǎng)絡(luò)通訊的網(wǎng)絡(luò)線程。那么,我們又如何把Socket的處理引入線程中?

                看看下面的代碼片段:

            HANDLE hEvent;
            hEvent = CreateEvent(NULL,0,0,0);
            // 設(shè)置異步通訊
            if( WSAEventSelect( sSocket, hEvent,
            FD_ACCEPT|FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE ) ==SOCKET_ERROR )
            {
             Failed( "WinSocket EventSelect Error!");
            }

                通過上面的設(shè)置之后,WinSock API函數(shù)均會以非阻塞方式運(yùn)行,也就是函數(shù)執(zhí)行后會立即返回,這時網(wǎng)絡(luò)通訊會以事件方式存儲于hEvent,而不會停頓整支程式。

                完成了上面的步驟之后,我們需要對事件進(jìn)行響應(yīng)與處理,讓我們看看如何在網(wǎng)絡(luò)線程中獲得網(wǎng)絡(luò)通訊所產(chǎn)生的事件消息:

            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)
               // 連接成功  
               {
               // 連接成功后通知主線程(游戲線程)進(jìn)行處理
               }
              }
               break;
              case FD_READ:
              // 獲取網(wǎng)絡(luò)數(shù)據(jù)
              {
               if( recv( sSocket, pBuffer, lLength, 0) == SOCKET_ERROR )
               {
                Failed( "WinSocket Recv Error!");
               }
              }
               break;
              case FD_WRITE:
               break;
              case FD_CLOSE:
               // 通知主線程(游戲線程), 網(wǎng)絡(luò)已經(jīng)斷開
               break;
              default:
            }
            }


                這里僅對網(wǎng)絡(luò)連接(FD_CONNECT) 和讀取數(shù)據(jù)(FD_READ) 進(jìn)行了簡單模擬操作,但實(shí)際中網(wǎng)絡(luò)線程接收到事件消息后,會對數(shù)據(jù)進(jìn)行組織整理,然后再將數(shù)據(jù)回傳給我們的游戲主線程使用,游戲主線程再將處理過的數(shù)據(jù)發(fā) 送出去,這樣一個往返就構(gòu)成了我們網(wǎng)絡(luò)游戲中的數(shù)據(jù)通訊,是讓網(wǎng)絡(luò)游戲動起來的最基本要素。

                最后,我們來談?wù)勱P(guān)于網(wǎng)絡(luò)數(shù)據(jù)包(數(shù)據(jù)封包)的組織,網(wǎng)絡(luò)游戲的數(shù)據(jù)包是游戲數(shù)據(jù)通訊的最基本單位,網(wǎng)絡(luò)游戲一般不會用字節(jié)流的方式來進(jìn)行數(shù)據(jù)傳輸,一個 數(shù)據(jù)封包也可以看作是一條消息指令,在游戲進(jìn)行中,服務(wù)器和客戶端會不停的發(fā)送和接收這些消息包,然后將消息包解析轉(zhuǎn)換為真正所要表達(dá)的指令意義并執(zhí)行。

                互動與管理

                說到互動,對于玩家來說是與其他玩家的交流,但對于計算機(jī)而言,實(shí)現(xiàn)互動也就是實(shí)現(xiàn)數(shù)據(jù)消息的相互傳遞。前面我們已經(jīng)了解過網(wǎng)絡(luò)通訊的基本概念,它構(gòu)成了 互動的最基本條件,接下來我們需要在這個網(wǎng)絡(luò)層面上進(jìn)行數(shù)據(jù)的通訊。遺憾的是,計算機(jī)并不懂得如何表達(dá)玩家之間的交流,因此我們需要提供一套可讓計算機(jī)了 解的指令組織和解析機(jī)制,也就是對我們上面簡單提到的網(wǎng)絡(luò)數(shù)據(jù)包(數(shù)據(jù)封包)的處理機(jī)制。

                為了能夠更簡單的給大家闡述網(wǎng)絡(luò)數(shù)據(jù)包的組織形式,我們以一個聊天處理模塊來進(jìn)行討論,看看下面的代碼結(jié)構(gòu):

            struct tagMessage{
             long lType;
             long lPlayerID;
            };
            // 消息指令
            // 指令相關(guān)的玩家標(biāo)識
            char strTalk[256]; // 消息內(nèi)容

                上面是抽象出來的一個極為簡單的消息包結(jié)構(gòu),我們先來談?wù)勂涓鱾€數(shù)據(jù)域的用途:首先,lType 是消息指令的類型,這是最為基本的消息標(biāo)識,這個標(biāo)識用來告訴服務(wù)器或客戶端這條指令的具體用途,以便于服務(wù)器或客戶端做出相應(yīng)處理。lPlayerID 被作為玩家的標(biāo)識。大家知道,一個玩家在機(jī)器內(nèi)部實(shí)際上也就是一堆數(shù)據(jù),特別是在游戲服務(wù)器中,可能有成千上萬個玩家,這時候我們需要一個標(biāo)記來區(qū)分玩 家,這樣就可以迅速找到特定玩家,并將通訊數(shù)據(jù)應(yīng)用于其上。

                strTalk 是我們要傳遞的聊天數(shù)據(jù),這部分才是真正的數(shù)據(jù)實(shí)體,前面的參數(shù)只是數(shù)據(jù)實(shí)體應(yīng)用范圍的限定。

                在組織完數(shù)據(jù)之后,緊接著就是把這個結(jié)構(gòu)體數(shù)據(jù)通過Socket 連接發(fā)送出去和接收進(jìn)來。這里我們要了解,網(wǎng)絡(luò)在進(jìn)行數(shù)據(jù)傳輸過程中,它并不關(guān)心數(shù)據(jù)采用的數(shù)據(jù)結(jié)構(gòu),這就需要我們把數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)碼進(jìn)行發(fā) 送,在接收方,我們再將這些二進(jìn)制數(shù)據(jù)碼轉(zhuǎn)換回程序使用的相應(yīng)數(shù)據(jù)結(jié)構(gòu)。讓我們來看看如何實(shí)現(xiàn):

            tagMessageMsg;
            Msg.lTypeMSG_CHAT;
            Msg.lPlayerID 1000;
            strcpy( &Msg.strTalk, "聊天信息" );

                首先,我們假設(shè)已經(jīng)組織好一個數(shù)據(jù)包,這里MSG_CHAT 是我們自行定義的標(biāo)識符,當(dāng)然,這個標(biāo)識符在服務(wù)器和客戶端要統(tǒng)一。玩家的ID 則根據(jù)游戲需要來進(jìn)行設(shè)置,這里1000 只作為假設(shè),現(xiàn)在繼續(xù):

            char* p = (char*)&Msg;
            long lLength = sizeof( tagMessage );
            send( sSocket, p, lLength );
            // 獲取數(shù)據(jù)結(jié)構(gòu)的長度

                我們通過強(qiáng)行轉(zhuǎn)換把結(jié)構(gòu)體轉(zhuǎn)變?yōu)閏har 類型的數(shù)據(jù)指針,這樣就可以通過這個指針來進(jìn)行流式數(shù)據(jù)處理,這里通過

                sizeof() 獲得結(jié)構(gòu)體長度,然后用WinSock 的Send() 函數(shù)將數(shù)據(jù)發(fā)送出去。

                接下來看看如何接收數(shù)據(jù):

            long lLength = sizeof( tagMessage );
            char* Buffer = new char[lLength];
            recv( sSocket, Buffer, lLength );
            tagMessage* p = (tagMessage*)Buffer;
            // 獲取數(shù)據(jù)

                在通過WinSock 的recv() 函數(shù)獲取網(wǎng)絡(luò)數(shù)據(jù)之后,我們同樣通過強(qiáng)行轉(zhuǎn)換把獲取出來的緩沖數(shù)據(jù)轉(zhuǎn)換為相應(yīng)結(jié)構(gòu)體,這樣就可以方便地對數(shù)據(jù)進(jìn)行訪問。(注:強(qiáng)行轉(zhuǎn)換僅僅作為數(shù)據(jù)轉(zhuǎn)換的 一種手段,實(shí)際應(yīng)用中有更多可選方式,這里只為簡潔地說明邏輯)談到此處,不得不提到服務(wù)器/ 客戶端如何去篩選處理各種消息以及如何對通訊數(shù)據(jù)包進(jìn)行管理。無論是服務(wù)器還是客戶端,在收到網(wǎng)絡(luò)消息的時候,通過上面的數(shù)據(jù)解析之后,還必須對消息類型 進(jìn)行一次篩選和派分,簡單來說就是類似Windows 的消息循環(huán),不同消息進(jìn)行不同處理。這可以通過一個switch 語句(熟悉Windows 消息循環(huán)的朋友相信已經(jīng)明白此意),基于消

                息封包里的lType 信息,對消息進(jìn)行區(qū)分處理,考慮如下代碼片段:

            switch( p->lType ) // 這里的p->lType為我們解析出來的消息類型標(biāo)識
            {
             case MSG_CHAT: // 聊天消息
              break;
             case MSG_MOVE: // 玩家移動消息
              break;
             case MSG_EXIT: // 玩家離開消息
              break;
             default:
              break;
            }

                面片段中的MSG_MOVE 和MSG_EXIT 都是我們虛擬的消息標(biāo)識(一個真實(shí)游戲中的標(biāo)識可能會有上百個,這就需要考慮優(yōu)化和優(yōu)先消息處理問題)。此外,一個網(wǎng)絡(luò)游戲服務(wù)器面對的是成百上千的連接 用戶,我們還需要一些合理的數(shù)據(jù)組織管理方式來進(jìn)行相關(guān)處理。普通的單體游戲服務(wù)器,可能會因為當(dāng)機(jī)或者用戶過多而導(dǎo)致整個游戲網(wǎng)絡(luò)癱瘓,而這也就引入分 組服務(wù)器機(jī)制,我們把服務(wù)器分開進(jìn)行數(shù)據(jù)的分布式處理。

                我們把每個模塊提取出來,做成專用的服務(wù)器系統(tǒng),然后建立一個連接所有服務(wù)器的數(shù)據(jù)中心來進(jìn)行數(shù)據(jù)交互,這里每個模塊均與數(shù)據(jù)中心創(chuàng)建了連接,保證了每個 模塊的相關(guān)性,同時玩家轉(zhuǎn)變?yōu)榕c當(dāng)前提供服務(wù)的服務(wù)器進(jìn)行連接通訊,這樣就可以緩解單獨(dú)一臺服務(wù)器所承受的負(fù)擔(dān),把壓力分散到多臺服務(wù)器上,同時保證了數(shù) 據(jù)的統(tǒng)一,而且就算某臺服務(wù)因為異常而當(dāng)機(jī)也不會影響其他模塊的游戲玩家,從而提高了整體穩(wěn)定性。分組式服務(wù)器緩解了服務(wù)器的壓力,但也帶來了服務(wù)器調(diào)度 問題,分組式服務(wù)器需要對服務(wù)器跳轉(zhuǎn)進(jìn)行處理,就以一個玩家進(jìn)行游戲場景跳轉(zhuǎn)作為討論基礎(chǔ):假設(shè)有一玩家處于游戲場景A,他想從場景A 跳轉(zhuǎn)到場景B,在游戲中,我們稱之場景切換,這時玩家就會觸發(fā)跳轉(zhuǎn)需求,比如走到了場景中的切換點(diǎn),這樣服務(wù)器就把玩家數(shù)據(jù)從"游戲場景A 服務(wù)器"刪除,同時在"游戲場景B 服務(wù)器"中把玩家建立起來。

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

                不過需要注意的是,在場景切換或者說模塊間切換的時候,需要切實(shí)考慮好數(shù)據(jù)的傳輸安全以及邏輯合理性,否則切換很可能會成為將來玩家復(fù)制物品的橋梁。

                總結(jié)

                本篇講述的都是通過一些簡單的過程來進(jìn)行網(wǎng)絡(luò)游戲通訊,提供了一個制作的思路,雖然具體實(shí)現(xiàn)起來還有許多要做 ,但只要順著這個思路去擴(kuò)展、去完善,相信大家很快就能夠編寫出自己的網(wǎng)絡(luò)通訊模塊。由于時間倉促,本文在很多細(xì)節(jié)方面都有省略,文中若有錯誤之處也望大 家見諒



            posted on 2009-09-23 23:39 暗夜教父 閱讀(1453) 評論(0)  編輯 收藏 引用 所屬分類: Game Development

            <2009年9月>
            303112345
            6789101112
            13141516171819
            20212223242526
            27282930123
            45678910

            常用鏈接

            留言簿(2)

            隨筆分類

            隨筆檔案

            文章分類

            文章檔案

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            伊人久久大香线蕉综合网站| 亚洲国产精品无码久久久蜜芽| 99久久精品免费国产大片| 无码任你躁久久久久久老妇App| 四虎国产精品免费久久5151| 久久久久久久亚洲Av无码| 亚洲伊人久久成综合人影院 | 91精品国产91久久久久久青草 | 久久天天躁狠狠躁夜夜不卡| 久久这里只有精品首页| 蜜桃麻豆www久久国产精品| 久久久久99精品成人片欧美| 欧洲国产伦久久久久久久| 亚洲精品无码久久久久AV麻豆| 色妞色综合久久夜夜| 久久国产免费观看精品3| 久久久青草青青国产亚洲免观| 亚洲国产精品无码久久| 国产精品熟女福利久久AV| 久久精品国产亚洲精品| 青青热久久国产久精品| 2021久久精品国产99国产精品| 2021少妇久久久久久久久久| 久久国产欧美日韩精品免费| 免费精品久久天干天干| 久久99精品久久久久久水蜜桃| 青青久久精品国产免费看| 伊人久久大香线蕉精品| 久久精品国产亚洲AV无码麻豆| 久久综合鬼色88久久精品综合自在自线噜噜 | 久久91精品久久91综合| 91久久精品视频| 2022年国产精品久久久久| 久久综合给合久久狠狠狠97色 | 国产精品va久久久久久久| 99久久精品国产高清一区二区| 一本一道久久综合狠狠老| 高清免费久久午夜精品| 男女久久久国产一区二区三区| 久久亚洲精品无码aⅴ大香| 日日狠狠久久偷偷色综合0|