MMO中的聊天服務(wù)主要功能就是做客戶端之間的聊天內(nèi)容轉(zhuǎn)發(fā)。但是聊天的形式有很多,例如私聊、同場景聊、隊(duì)伍內(nèi)聊、工會(huì)內(nèi)聊、全服務(wù)器聊、甚至臨 時(shí)組建房間聊。這些邏輯功能其實(shí)都是可以做在邏輯服務(wù)器上的,最多改改世界服務(wù)器,但是這樣完成功能的話,不免將聊天本身的邏輯與游戲邏輯關(guān)聯(lián)起來。我們 希望做得更上一層,將聊天服務(wù)本身脫離開來。但是獨(dú)立聊天服務(wù)還不夠,因?yàn)榫退悛?dú)立出來了,也有可能在實(shí)現(xiàn)上與具體的游戲邏輯相關(guān)聯(lián)。所以,我們做了進(jìn)一 步的抽象,想實(shí)現(xiàn)一個(gè)更為通用的聊天服務(wù)器。
設(shè)計(jì)實(shí)現(xiàn)
實(shí)體設(shè)計(jì)
聊天這個(gè)過程,我們將其抽象為實(shí)體(entity)與實(shí)體間的對(duì)話。這個(gè)實(shí)體概念其實(shí)很寬泛。任何可接收聊天消息的都算做實(shí)體,例如單個(gè)玩家、一個(gè) 場景、一個(gè)隊(duì)伍、一個(gè)房間、一個(gè)工會(huì)、甚至整個(gè)服務(wù)器。這個(gè)思想其實(shí)就是支持整個(gè)聊天服務(wù)器設(shè)計(jì)的最根本思想。最開始,我將聊天服務(wù)器分為個(gè)體和組兩個(gè)概 念,其實(shí)這個(gè)抽象程度都太低,并且會(huì)導(dǎo)致實(shí)現(xiàn)上的復(fù)雜。相反,將整個(gè)系統(tǒng)完全使用實(shí)體這個(gè)概念來組裝,就簡單很多。當(dāng)然,實(shí)體是有很多種類的,在處理接收 聊天消息這個(gè)動(dòng)作時(shí),其處理方式就不同。例如單個(gè)玩家實(shí)體僅做消息的發(fā)送,場景實(shí)體則是將消息發(fā)給場景內(nèi)的所有玩家,隊(duì)伍實(shí)體就是將消息發(fā)給隊(duì)伍內(nèi)的所有 玩家。從這一點(diǎn)來看,我們的實(shí)體種類其實(shí)并不多,因?yàn)閳鼍?、?duì)伍這些,都是組實(shí)體(group entity)。用C++來描述:
class Entity {
public:
// send text to this entity
virtual bool Send(Entity *sender, const std::string &text) = 0;
protected:
GUID m_id;
int m_type;
};
class SockEntity : pubilc Entity {
public:
virtual bool Send(Entity *sender, const std::string &text) {
// find the map socket and send text to the socket
long socket = FindSocket(this);
Message msg(MSG_CS2E_SENDTEXT);
msg.Add(sender->ID());
msg.Add(text);
msg.SendToSocket(socket);
return true;
}
};
class GroupEntity : public Entity {
public:
virtual bool Send(Entity *sender, const std::string &text) {
for (std::list<Entity*>::const_iterator it = m_mems.begin(); it != m_mems.end(); ++it) {
(*it)->Send(sender, text);
}
return true;
}
private:
std::list<Entity*> m_mems;
};
SockEntity
用于表示物理上聊天服務(wù)器的客戶端,例如游戲客戶端。
網(wǎng)絡(luò)拓?fù)?/h3>
實(shí)際上,除了轉(zhuǎn)發(fā)聊天內(nèi)容外(Entity::Send),實(shí)體還有很多其他行為,例如最起碼的,創(chuàng)建組實(shí)體,往組實(shí)體里添加成員等。在設(shè)計(jì)上,組 實(shí)體的創(chuàng)建由邏輯服務(wù)器或者其他服務(wù)器來完成,目前游戲客戶端是沒有創(chuàng)建組實(shí)體的權(quán)限的(實(shí)現(xiàn)上我們還為實(shí)體添加了權(quán)限驗(yàn)證機(jī)制)。在網(wǎng)絡(luò)拓?fù)渖?,聊天?務(wù)器始終是作為服務(wù)器角色,而它的客戶端則包括游戲客戶端、邏輯服務(wù)器、甚至其他服務(wù)器,這樣聊天服務(wù)器在提供了固定的協(xié)議后,它就是完全獨(dú)立的,不依賴 任何其他組件:
CS
/ | \
/ | \
/ | \
GC GC GS
(CS: Chat Server, GC: Game Client, GS: Game Server)
基于此,我們擴(kuò)充了Entity的類體系:
class ClientEntity : public SockEntity {
private:
GUID m_gsEntity; // 標(biāo)示該客戶端實(shí)體位于哪個(gè)邏輯服務(wù)器實(shí)體上
};
class GSEntity : public SockEntity {
};
消息協(xié)議
聊天服務(wù)器的核心實(shí)現(xiàn),其實(shí)就是針對(duì)以上實(shí)體做操作。因此,聊天服務(wù)器的消息協(xié)議方面,也主要是針對(duì)這些實(shí)體的操作,包括:
-
創(chuàng)建
實(shí)體的創(chuàng)建很簡單,不同的實(shí)體其創(chuàng)建所需的參數(shù)都不一樣。例如客戶端實(shí)體創(chuàng)建時(shí)需要傳入一個(gè)邏輯服務(wù)器實(shí)體的ID,組實(shí)體的創(chuàng)建可以攜帶組成員實(shí)體列表。 為了處理權(quán)限和安全問題,在具體實(shí)現(xiàn)上,邏輯服務(wù)器實(shí)體的創(chuàng)建是由聊天服務(wù)器本地的配置決定,即聊天服務(wù)器啟動(dòng)則根據(jù)配置創(chuàng)建好邏輯服務(wù)器實(shí)體;客戶端實(shí) 體是當(dāng)角色進(jìn)入邏輯服務(wù)器后,由服務(wù)器創(chuàng)建,客戶端無法創(chuàng)建實(shí)體。
-
刪除
實(shí)體的刪除為了處理方便,約定刪除請(qǐng)求必須由實(shí)體的創(chuàng)建者發(fā)起。因?yàn)閺倪壿嬌蠈?,某個(gè)模塊如果可以創(chuàng)建一個(gè)實(shí)體,那么其必然知道什么時(shí)候該刪除這個(gè)實(shí)體。
-
修改
修改指的是修改實(shí)體內(nèi)部實(shí)現(xiàn)的一些屬性,例如組實(shí)體修改其組成員。這個(gè)操作是非常重要的。對(duì)于SockEntity
而 言,修改意味著修改其連接狀態(tài),例如當(dāng)邏輯服務(wù)器在聊天服務(wù)器上創(chuàng)建了客戶端實(shí)體后,實(shí)際上此時(shí)客戶端并沒有在網(wǎng)絡(luò)方面連接聊天服務(wù)器,此時(shí)這個(gè)Entity
實(shí) 際上是不可用的,因?yàn)樗鼰o法用于發(fā)送消息。這個(gè)時(shí)候我們標(biāo)志該實(shí)體的狀態(tài)為非連接狀態(tài)。當(dāng)客戶端主動(dòng)連接上聊天服務(wù)器后,客戶端就主動(dòng)發(fā)起修改自己對(duì)應(yīng)的 客戶端實(shí)體請(qǐng)求,該請(qǐng)求將自己的狀態(tài)修改為連接狀態(tài)。當(dāng)客戶端關(guān)閉時(shí),聊天服務(wù)器網(wǎng)絡(luò)層接收到連接斷開通知,該通知肯定是早于邏輯服務(wù)器發(fā)來的刪除實(shí)體通 知的,此時(shí)將該客戶端實(shí)體狀態(tài)修改為斷開狀態(tài),并在接收到邏輯服務(wù)器刪除實(shí)體通知時(shí)將其真正刪除。這里展示的這種狀態(tài)修改策略,實(shí)際上在整個(gè)系統(tǒng)中是非常 重要的。它用于指導(dǎo)網(wǎng)絡(luò)連接和上層邏輯之間的關(guān)系,因?yàn)檎麄€(gè)聊天系統(tǒng)中,各個(gè)進(jìn)程的狀態(tài)是不可預(yù)料的(隨時(shí)可能宕掉),當(dāng)某個(gè)進(jìn)程尤其是邏輯服務(wù)器宕掉 后,聊天服務(wù)器是得不到任何正常邏輯通知的,它只能得到網(wǎng)絡(luò)連接的通知。
總結(jié)
整個(gè)系統(tǒng)實(shí)現(xiàn)下來,實(shí)際上是非常簡單的,代碼量也很少。當(dāng)然還有很多細(xì)節(jié)問題,例如聊天信息中攜帶物品信息,這涉及到異步預(yù)處理聊天內(nèi)容,這里就不 方便細(xì)說了。
原文地址: http://codemacro.com/2012/08/29/mmo-chat-server/
written by Kevin Lynx posted at http://codemacro.com