【FOL】第九周

时间:2023-12-22 13:14:38

不知不觉又是三周过去了。

这几周忙了一下,其他时间全都在搞服务端,简直是酸爽的不行。。。不过还好出了些成果。

目前服务端有:

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:= ;
      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));
           then exit;
          //更新freenum(freenum+1)
          sfLogger.logMessage('[INFO]: Client offline, userid='+lvUserID);
          lvServerKey:= Format('server:%.3d',[lvServerID]);
          redisClient.HINCRBY(lvServerKey,);
          //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,[',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
    do
  begin
    lvServerKey:= Format('server:%.3d',[i]);
    lvData:= redisClient.HMGET(lvServerKey, ['name', 'state']);
    ) ]='') then break;
    pvMsgData.Add(lvData[], StrToInt(lvData[]));
  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
    : result:= MSG_RESULT_Queue;
   -: 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);
     then raise Exception.Create('Service is Closed.');
    redisClient.HGET(lvServerKey,'freenum',lvFreeNum);
     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 - ,);
    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.