不知不覺又是三周過去了。
這幾周忙了一下,其他時間全都在搞服務端,簡直是酸爽的不行。。。不過還好出了些成果。
目前服務端有:
1、版本服務:游戲版本更新
2、賬號服務:用戶身份驗證,返回各種連接(代理服務、聊天服務)
3、代理服務:獲取游服列表、獲取游服狀態(是否需要排隊)、進入游戲的驗證、游服數據轉發
4、隊列服務:處理排隊隊列中用戶的隊列情況變化,並廣播(本來這個是放在代理服務器上做的,但是我覺得廣播起來有點惡心就分出來了,看起來各個服務的功能也清晰些)
5、游戲服務:游戲業務邏輯處理(作為1-n個特殊的客戶端連接到代理服務器)
6、聊天服務
客戶端方面,優化了一些網絡通訊方面的代碼,把各種消息重新整理了一遍。界面表現方面幾乎沒動過。
整個框架基本上實現了登錄、選服、排隊(等待、結束)、進入游戲、聊天。
想了一下,還是放段代碼。
unit gate.handler; interface uses System.SysUtils, System.Classes, System.Math, diocp_coder_tcpServer, diocp_tcp_server, fol.msgcode, fol.types, fol.simpleMsgPack, fol.server.types, fol.server.session, gate.session, gate.cache; procedure pushMsgData(pvMsgData: TSimpleMsgPack; pvContext: TIocpClientContext); procedure execHeart(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext); procedure execOffline(pvContext: TIOCPCoderClientContext); function execRequest(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): Integer; function execRegister(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): Integer; function execRegisterGameServiceClient(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): string; function execRequestServerList(pvMsgData: TSimpleMsgPack): Integer; function execRequestServerState(const pvServerID: Integer; pvMsgData: TSimpleMsgPack): Integer; function execRequestStartGame(const pvServerID,pvUserID: Integer; pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): Integer; function execRequestGameService(pvMsgData: TSimpleMsgPack): Integer; function execTranspond(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): Integer; implementation uses utils_safeLogger; procedure pushMsgData(pvMsgData: TSimpleMsgPack; pvContext: TIocpClientContext); var lvStream: TMemoryStream; begin lvStream:= TMemoryStream.Create; try if Assigned(pvContext) then begin pvMsgData.Add('result',MSG_RESULT_Success); pvMsgData.EncodeToStream(lvStream); lvStream.Position:= 0; TIOCPCoderClientContext(pvContext).WriteObject(lvStream); end; finally lvStream.Free; end; end; procedure execHeart(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext); begin if pvContext.LockContext('keeplive', nil) then try gvSessionManager.validChecked(pvContext); pvMsgData.Clear; finally pvContext.UnLockContext('keeplive', nil); end; end; procedure execOffline(pvContext: TIOCPCoderClientContext); var lvServerID: Integer; lvUserID, lvServerKey: string; begin if pvContext.Data <> nil then begin { 1. 游戲客戶端: 只是連接了代理服務器,並未選服登錄游戲 (斷開不需要處理) 2. 游戲客戶端: 已經進入游戲的,斷開需要更新對應服務器freenum 3. OM工具: 斷開不需要處理 4. 游戲服務: 斷開需要把游服記錄刪除(或者更新state) } lvServerID:= TClientSession(pvContext.Data).ServerID; lvUserID:= TClientSession(pvContext.Data).SessionID; case TClientSession(pvContext.Data).ClientType of tfctGameClient: begin sfLogger.logMessage('[INFO]: Client offline, serverid='+inttostr(lvServerID)); if lvServerID = 0 then exit; //更新freenum(freenum+1) sfLogger.logMessage('[INFO]: Client offline, userid='+lvUserID); lvServerKey:= Format('server:%.3d',[lvServerID]); redisClient.HINCRBY(lvServerKey,'freenum',1); //kickOut這個session(或者設置為無效session) gvSessionManager.kickOutGameClient(lvUserID); end; tfctGameServiceClient: begin //更新游服列表 lvServerKey:= Format('server:%.3d',[StrToInt(lvUserID)]); redisClient.DEL([lvServerKey]); gvSessionManager.kickOutGameService(lvUserID); end; tfctOMClient: begin sfLogger.logMessage('[INFO]: OMClient offline'); gvSessionManager.kickOutOMClient(lvUserID); end; end; end; end; function execRegister(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): Integer; var lvClientType: Integer; lvSessionID: string; begin if pvContext.LockContext('register', nil) then try lvClientType:= pvMsgData.I['client_type']; case lvClientType of ord(tfctGameClient): lvSessionID:= gvSessionManager.takeAGameClientSession(pvContext, pvMsgData.S['client_id']); ord(tfctGameServiceClient): lvSessionID:= execRegisterGameServiceClient(pvMsgData, pvContext); ord(tfctOMClient): lvSessionID:= gvSessionManager.takeAOMClientSession(pvContext, pvMsgData.S['client_id']); end; pvMsgData.Clear; pvMsgData.S['token']:= '(gate)token_'+lvSessionID; result:= MSG_RESULT_Success; finally pvContext.UnLockContext('register', nil); end; end; function execRegisterGameServiceClient(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): string; var lvServiceID, lvServiceName, lvServerKey, lvMaxConn: string; begin lvServiceID:= pvMsgData.S['client_id']; lvServiceName:= pvMsgData.S['client_name']; lvMaxConn:= pvMsgData.S['client_maxconn']; result:= gvSessionManager.takeAGameServiceSession(pvContext,lvServiceID); //example: hmset server:001 name 五行之始 state 1 freenum 1000 lvServerKey:= Format('server:%.3d',[StrToInt(lvServiceID)]); redisClient.HMSET(lvServerKey,['name','state','freenum'],[lvServiceName,'0',lvMaxConn]); end; function execRequest(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): Integer; var lvMsgcode, lvCallbackID, lvServerID, lvUserID: Integer; begin lvMsgcode:= pvMsgData.I['msg_code']; lvCallbackID:= pvMsgData.I['callbackid']; sfLogger.logMessage('[INFO]: Receive a message package, msgcode=' + inttostr(lvMsgcode)); case lvMsgcode of MSG_NET_Register: result:= execRegister(pvMsgData, pvContext); MSG_NET_GetServerList: begin pvMsgData.Clear; result:= execRequestServerList(pvMsgData); end; MSG_NET_GetServerState: begin lvServerID:= pvMsgData.I['serverid']; pvMsgData.Clear; result:= execRequestServerState(lvServerID,pvMsgData); end; MSG_NET_StartGame: begin lvServerID:= pvMsgData.I['serverid']; lvUserID:= pvMsgData.I['userid']; pvMsgData.Clear; result:= execRequestStartGame(lvServerID,lvUserID,pvMsgData,pvContext); end; else begin execRequestGameService(pvMsgData); pvMsgData.Clear; exit; end; end; pvMsgData.Add('callbackid', lvCallbackID); end; function execRequestServerList(pvMsgData: TSimpleMsgPack): Integer; var i: Integer; lvServerKey: string; lvData: TArray<string>; begin for i:= 1 to 999 do begin lvServerKey:= Format('server:%.3d',[i]); lvData:= redisClient.HMGET(lvServerKey, ['name', 'state']); if (Length(lvData)=0) or (lvData[0]='') then break; pvMsgData.Add(lvData[0], StrToInt(lvData[1])); end; result:= MSG_RESULT_Success; end; function execRequestServerState(const pvServerID: Integer; pvMsgData: TSimpleMsgPack): Integer; var lvServerKey: string; lvFreeNum: Integer; begin lvServerKey:= Format('server:%.3d',[pvServerID]); redisClient.HGET(lvServerKey,'freenum',lvFreeNum); case lvFreeNum of 0: result:= MSG_RESULT_Queue; -1: raise exception.Create('invalid freenum.'); else result:= MSG_RESULT_Success; end; end; function execRequestStartGame(const pvServerID,pvUserID: Integer; pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): Integer; var lvServerKey, lvQueueKey: string; lvFreeNum, lvState: Integer; begin if pvContext.Data <> nil then begin lvServerKey:= Format('server:%.3d',[pvServerID]); redisClient.HGET(lvServerKey,'state',lvState); if lvState <> 0 then raise Exception.Create('Service is Closed.'); redisClient.HGET(lvServerKey,'freenum',lvFreeNum); if lvFreeNum <= 0 then raise Exception.Create('Invalid request, free num is zero.'); // TClientSession(pvContext.Data).ServerID:= pvServerID; TClientSession(pvContext.Data).State:= tusOnline; //更新freenum(freenum-1) lvFreeNum:= Max(lvFreeNum - 1,0); redisClient.HSET(lvServerKey,'freenum',lvFreeNum); //廣播上線消息給好友 end; result:= MSG_RESULT_Success; end; function execRequestGameService(pvMsgData: TSimpleMsgPack): Integer; var lvServerID: string; lvContext: TIocpClientContext; begin lvServerID:= IntToStr(pvMsgData.I['serverid']); lvContext:= gvSessionManager.findGameServiceContext(lvServerID); pushMsgData(pvMsgData,lvContext); end; function execTranspond(pvMsgData: TSimpleMsgPack; pvContext: TIOCPCoderClientContext): Integer; var lvUserID: string; lvContext: TIocpClientContext; begin lvUserID:= pvMsgData.S['userid']; lvContext:= gvSessionManager.findGameClientContext(lvUserID); pushMsgData(pvMsgData,lvContext); end; end.