匹配
//請(qǐng)求匹配
message CM_Match {
int type; //匹配房間類型
String name; //玩家名稱
long targetPlayerId; //加入他人比賽,目標(biāo)玩家id
}
//匹配成功
message L2RM_MatchSucc {
int roomId;
int roomType;
boolean createRoomIfNotExist; //場(chǎng)景中的第一個(gè)玩家為true
RoomPlayerEnt ent; //房間服需要的玩家信息
}
//業(yè)務(wù)服收到此消息創(chuàng)建房間
message R2LM_AddRoom {
int id;
int sign; //房間簽名
int type;
Date createTime;
}
//業(yè)務(wù)服收到此消息,在房間銷毀前(R2LM_RemoveRoom),玩家都可以斷線重連房間服
message R2LM_AddPlayer {
long playerId;
int roomId;
int roomSign;
int roomType;
int token; //登錄令牌,斷線重連時(shí)下發(fā)給客戶端
}
//客戶端收到此消息后連接房間服
message SM_RoomServerPermission {
String host;
int port;
String token; //本次登錄的令牌
}
業(yè)務(wù)服房間管理
業(yè)務(wù)服維護(hù)了房間服的Room。房間服創(chuàng)建Room,業(yè)務(wù)服創(chuàng)建對(duì)應(yīng)Room,房間服銷毀Room,業(yè)務(wù)服銷毀對(duì)應(yīng)Room。其變化通過處理房間服的R2LM_AddRoom,R2LM_RemoveRoom等消息完成。
在匹配規(guī)則中另外維護(hù)了MatchRoom。匹配規(guī)則認(rèn)為需要?jiǎng)?chuàng)建一個(gè)新的房間時(shí),則馬上創(chuàng)建MatchRoom,當(dāng)一個(gè)玩家被匹配到某個(gè)MatchRoom中,MatchRoom中立即添加該玩家。新增過程不依賴房間服的消息。
Room的作用是用于統(tǒng)計(jì)房間服的信息,玩家的斷線重連。MatchRoom用于完成匹配邏輯。兩者的作用不同決定生命周期不同,Room的生命周期由房間服決定,MatchRoom在匹配時(shí)創(chuàng)建,在人滿時(shí)銷毀,在房間服的房間銷毀時(shí)也會(huì)銷毀。
class Room {
int id;
int sign;
RoomType type;
Date createTime;
int playerNum; //玩家數(shù)量
int watcherNum; //觀戰(zhàn)者數(shù)量
RoomServer server; //所屬房間服
}
class MatchRoom {
int id;
List<Long> playerIds;
RoomServer server; //所屬房間服
}
進(jìn)入房間服
message CM_EnterRoom {
long playerId;
String token; //登錄令牌
}
//業(yè)務(wù)服收到此消息,將玩家加入房間服玩家集合
message R2LM_PlayerEnterRoom {
long playerId;
int roomId;
int roomType;
int status; //狀態(tài) 0.游戲 1.觀戰(zhàn)
}
//場(chǎng)景快照
message SM_SceneSnapshot {
}
離開房間服
//業(yè)務(wù)服收到此消息,將玩家從房間服玩家集合中移除
message R2LM_PlayerLeaveRoom {
long playerId;
int roomId;
String token; //登錄令牌相同才能移除玩家
}
玩家結(jié)算
//結(jié)算消息分成兩部分,第一部分在房間服計(jì)算,比如排行榜
message SM_RoomResult {
根據(jù)結(jié)算面板確定...
}
//通知客戶端斷開房間服連接,返回業(yè)務(wù)服
message SM_DisconnectRoomServer {
連接業(yè)務(wù)服的信息...
}
//將結(jié)算內(nèi)容發(fā)到業(yè)務(wù)服,由業(yè)務(wù)服計(jì)算獎(jiǎng)勵(lì)等數(shù)據(jù)
message R2LM_PlayerResult {
}
//業(yè)務(wù)服收到此消息,清理玩家斷線重連的相關(guān)信息。
//房間服再次收到R2LM_AddPlayer消息才允許玩家登陸。
message R2LM_RemovePlayer {
long playerId;
String token; //登錄令牌相同才能移除玩家
}
房間結(jié)算
//房間結(jié)算,包含了所有需要結(jié)算的玩家
message R2LM_RoomResult {
}
//業(yè)務(wù)服收到此消息,清理房間服
message R2LM_RemoveRemove {
long playerId;
String token; //登錄令牌相同才能移除玩家
}
房間結(jié)算時(shí),所有玩家必須結(jié)算。
房間服消息作用
房間服發(fā)給業(yè)務(wù)服的消息主要有6個(gè)
- R2LM_AddRoom
- R2LM_RemoveRoom
- R2LM_AddPlayer
- R2LM_RemovePlayer
- R2LM_PlayerEnterRoom
- R2LM_PlayerLeaveRoom
R2LM_AddRoom/R2LM_RemoveRoom用于維護(hù)業(yè)務(wù)服的房間的創(chuàng)建和銷毀。
R2LM_AddPlayer/R2LM_RemovePlayer主要用于維護(hù)玩家能否斷線重連房間服。
R2LM_PlayerEnterRoom/R2LM_PlayerLeaveRoom用于維護(hù)哪些玩家在房間服,以及相關(guān)狀態(tài)的修改。
玩家離開房間服的行為會(huì)導(dǎo)致房間服發(fā)送R2LM_PlayerLeaveRoom給業(yè)務(wù)服,而玩家完成一局游戲才會(huì)導(dǎo)致房間服發(fā)送R2LM_RemovePlayer給業(yè)務(wù)服。
房間服線程模型
每個(gè)房間綁定到線程池中的一個(gè)線程,房間的所有業(yè)務(wù)都單線程處理。
房間支持消息隊(duì)列,可以向其投遞各種消息。玩家進(jìn)入房間,玩家離開房間,玩家結(jié)算,房間結(jié)算等任務(wù),都在房間線程中處理。
第一個(gè)玩家進(jìn)入房間時(shí),啟動(dòng)房間定時(shí)器,并處理房間消息。房間銷毀后停止定時(shí)器,不再處理房間消息。
class Room {
ConcurrentLinkedQueue<IRoomTask> taskQueue;
}
<b>問題:</b>
當(dāng)房間服收到L2RM_MatchSucc時(shí),會(huì)創(chuàng)建新玩家,將玩家放入緩存,如果緩存中已存在玩家,則需要銷毀已存在的玩家。銷毀玩家的任務(wù)需要投遞到房間線程中執(zhí)行,可能會(huì)發(fā)生執(zhí)行順序錯(cuò)誤問題。
- 代碼順序
Player oldPlayer = playerMap.put(playerId, player);
if(oldPlayer != null && oldPlayer.getScene() != null) {
oldPlayer.getScene().removePlayerAsync(oldPlayer);
...
}
send R2LM_AddPlayer message
- 執(zhí)行順序
thread 1: send R2LM_AddPlayer message
thread 2: send R2LM_RemovePlayer message //removePlayer是異步執(zhí)行
<b>解決方法:</b>
并不糾正執(zhí)行順序,而是在R2LM_AddPlayer和R2LM_RemovePlayer消息中增加token字段。player和oldPlayer的玩家id相同,但是登錄token不同。業(yè)務(wù)服通過對(duì)比token,可以知道本次R2LM_RemovePlayer是否有效,如果在R2LM_RemovePlayer之前已經(jīng)收到了包含新的token的R2LM_AddPlayer消息,則忽略R2LM_RemovePlayer消息的處理。
由于執(zhí)行順序依然是異步的,在同一時(shí)間,房間服可能同時(shí)存在相同id的兩個(gè)Player。所以除了在全局維護(hù)Player集合,每個(gè)房間還維護(hù)了自己的Player集合,房間中需要獲取玩家通過內(nèi)部的Player集合獲取,不要通過全局的Player集合獲取,因?yàn)楂@取到的可能是新的Player。
異常情況
L2RM_MatchSucc問題
1.玩家匹配到一個(gè)已經(jīng)銷毀的房間
解決方案
1.盡可能在房間銷毀前停止匹配,比如持續(xù)12分鐘的房間,可以在最后30s停止匹配。
2.提示錯(cuò)誤。玩家手動(dòng)重新匹配。
CM_EnterRoom問題
1.玩家不存在,或登錄令牌錯(cuò)誤
解決方案:報(bào)錯(cuò)。
2.房間不存在
解決方案:報(bào)錯(cuò),重連業(yè)務(wù)服。
3.投遞消息時(shí)房間存在,(在房間線程)執(zhí)行消息時(shí)房間不存在
解決方案:報(bào)錯(cuò),重連業(yè)務(wù)服。
只匹配,但不登錄房間服的玩家如何清理
定時(shí)30分鐘檢查,Player關(guān)聯(lián)的房間銷毀則清理Player。
沒有啟動(dòng)定時(shí)器的房間如何銷毀
定時(shí)30分鐘檢查,30分鐘都沒有玩家進(jìn)入則銷毀房間。