zoukankan      html  css  js  c++  java
  • DELPHI版传奇引擎学习菜鸟篇(applem2)05

    1-4是大概把GAMECENTER过了一遍,终于把消息机制入了一点门,接下来是服务端第一个服务的学习--DBServer.是一个数据库服务器,在学习这个单元的时候,发现了这个端的大概由来,不知道是哪个大牛反编译后重写的,看来之前我理解的是错误的,代码杂乱的原因不是没有考虑到正题设计,这是由DEDEDARK反编译的端,根据自己的经验补写的实现代码,不知道我这辈子能不能达到这样的水平,那得需要对汇编多熟悉才行啊.

    function TFrmNewChr.sub_49BD60(var sChrName: string): Boolean;//反编译的函数
    //0x0049BD60
    begin
      Result := False;
      EdName.Text := '';
      Self.ShowModal;
      sChrName := Trim(EdName.Text);
      if sChrName <> '' then
        Result := True;
    end;
    //这个函数是增加新角色,能看到汇编代码痕迹,单元里边还有DEDEDARK的注释说明

    4 DBServer

    4.1 DBSMain.pas

    先说主单元,这是整个传奇服务端的数据库服务器,这个单元结构还是比较清晰的,代码1500行左右,接口部分声明的新对象也不多,主要是VCL声明和过程,但是这个服务调用的其他模块较多,主要用于数据库(人物,物品,技能等)的处理.

    unit DBSMain;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      StdCtrls, ExtCtrls, JSocket, Buttons, IniFiles, Menus, Grobal2, HumDB, DBShare,
      ComCtrls, ActnList, AppEvnts, DB, DBTables, Common;
    
    type
      {定义服务器信息}
      TServerInfo = record
        nSckHandle: Integer;//socket句柄
        RecvBuff: PChar;    //接收数据缓冲区
        BuffLeng: Integer;  //缓冲区大小
        Socket: TCustomWinSocket;//这里直接继承的TCustomWinSocket,现在应该不用这样了
      end;
    
      pTServerInfo = ^TServerInfo;//定义为指针
    
      TFrmDBSrv = class(TForm)
        ServerSocket: TServerSocket;
        Timer1: TTimer;
        {.......中间的省略,都是VLC的声明}
        procedure X1Click(Sender: TObject);
        procedure N3Click(Sender: TObject);
        procedure F1Click(Sender: TObject);
        procedure T2Click(Sender: TObject);
        procedure MENU_MANAGE_TOOLClick(Sender: TObject);
      private
        n344: Integer;//这两个暂时还不知道
        n348: Integer;
        ServerList: TList; //服务器列表信息
        m_boRemoteClose: Boolean; //连接标志
        procedure ProcessServerPacket(ServerInfo: pTServerInfo);//数据包处理过程
        {发送数据}
        procedure SendSocket(Socket: TCustomWinSocket; SendBuff: PChar; BuffLen: Integer);
        {这是读取角色数据的过程,带有非法连接处理}
        procedure LoadHumanRcd(RecvBuff: PChar; BuffLen, QueryID: Integer; Socket: TCustomWinSocket);
        {角色退出时保存角色数据}
        procedure SaveHumanRcd(nRecog, QueryID: Integer; RecvBuff: PChar; BuffLen: Integer; Socket: TCustomWinSocket);
        {清理}
        procedure ClearSocket(Socket: TCustomWinSocket);
        {获取端口列表}
        procedure ShowModule();
        {加载物品数据库}
        function LoadItemsDB(): Integer;
        {加载技能数据库}
        function LoadMagicDB(): Integer;
      public
        {复制人物数据}
        function CopyHumData(sSrcChrName, sDestChrName, sUserId: string): Boolean;
        {删除人物数据}
        procedure DelHum(sChrName: string);
        {服务器消息处理函数,用于和其他进程通信}
        procedure MyMessage(var MsgData: TWmCopyData); message WM_COPYDATA;
      end;
    
    var
      FrmDBSrv: TFrmDBSrv;

    大部分的处理过程是针对SOCKET的编程和数据库的读写,开始我还觉得为什么不用大型数据库,看完之后,大概了解到传奇服务端数据本来就不多,这个服务端是为多机架设而写的,一般数据库,网关服务都在单独的服务器上,所以单个服务端最大在线一般不超过2000人,所以用小型的DBC2000还是足够的,大型数据库除非是专门的数据库服务器,那样会提高并发操作的效率,可是对于这套架构来说,无异于所有代码都需要重构了,先学习基本的数据处理方式,学到经验还可以用到多层数据库开发中,这也算一个捷径吧.

    4.2 DBShare.pas

    对于数据库服务器的共享数据单元倒没有什么特别的,主单元的数据处理函数一部分放在了这里,其他的大部分都是变量.

    unit DBShare;
    
    interface
    
    uses
      Windows, Messages, Classes, SysUtils, StrUtils, JSocket, WinSock, IniFiles,
      Grobal2, MudUtil, Common;
    
    const
      g_sUpDateTime = '修改日期: 2015/12/09';
      SIZEOFTHUMAN = 44145; //44032
    type
      TGList = class(TList)
      private
        GLock: TRTLCriticalSection;
      public
        constructor Create;
        destructor Destroy; override;
        procedure Lock;
        procedure UnLock;
      end;
    
      TSockaddr = record   //用于攻击检测用的
        nIPaddr: Integer;
        dwStartAttackTick: LongWord;
        nAttackCount: Integer;
      end;
    
      pTSockaddr = ^TSockaddr;
    
      TCheckCode = record    //测试用的,正式端不包含
        dwThread0: LongWord;
      end;
    
      TGateInfo = record       //网关信息
        Socket: TCustomWinSocket;
        sGateaddr: string; //0x04
        sText: string; //0x08
        sSendMsg: string;
        UserList: TList; //0x0C
        dwTick10: LongWord; //0x10
        nGateID: Integer; //网关ID
      end;
    
      pTGateInfo = ^TGateInfo;
    
      TUserInfo = record          //角色信息
        sAccount: string; //0x00
        sUserIPaddr: string; //0x0B
        sGateIPaddr: string;
        sConnID: string; //0x20
        sSockIndex: string;
        nSessionID: Integer; //0x24
        Socket: TCustomWinSocket;
        boChrSelected: Boolean; //0x30
        boChrQueryed: Boolean; //0x31
        dwTick34: LongWord; //0x34
        dwChrTick: LongWord; //0x38
        nSelGateID: ShortInt; //角色网关ID
        nDataCount: Integer;
        boWaitMsg: Boolean;
        nWaitID: Integer;
        sCreateChrMsg: string;
      end;
      pTUserInfo = ^TUserInfo;
    
      TRouteInfo = record      //路由配置信息
        nGateCount: Integer;
        sSelGateIP: string[15];
        sGameGateIP: array[0..7] of string[15];
        nGameGatePort: array[0..7] of Integer;
      end;
    
      pTRouteInfo = ^TRouteInfo;
    
    procedure LoadConfig();    //加载服务端设置
    
    procedure LoadIPTable();   //从设置文件里边加载IP列表
    
    function GetCodeMsgSize(X: Double): Integer; //取得消息编号
    
    function InClearMakeIndexList(nIndex: Integer): Boolean; //
    
    procedure WriteLogMsg(sMsg: string); //写入日志信息
    
    function CheckServerIP(sIP: string): Boolean;  //监测连接IP的合法性
    
    procedure SendGameCenterMsg(wIdent: Word; sSendMsg: string); //向引擎控制台发送消息
    
    procedure MainOutMessage(sMsg: string);  //发送消息到主界面
    
    function GetMagicName(wMagicId: Word): string; //获取技能名称
    
    function GetStdItemName(nPosition: Integer): string;//取得物品名称
    
    function CheckFiltrateUserName(sName: string): Boolean;//检查过滤角色
    
    procedure LoadFiltrateName(); //读取过滤
    
    function GetWaitMsgID(): Integer;//取得等待处理的消息编号
    
    var
      sDataDBFilePath: string = '.\DB\';
      nServerPort: Integer = 6000;
      sServerAddr: string = '0.0.0.0';
      g_nGatePort: Integer = 5100;
      g_sGateAddr: string = '0.0.0.0';
      nIDServerPort: Integer = 5600;
      sIDServerAddr: string = '127.0.0.1';
      g_nWaitMsgIndex: Integer = 0;
      g_boTestServer: Boolean = True;
      {以下暂时还不知道是干什么的,先不做猜测}
      HumDB_CS: TRTLCriticalSection; //0x004ADACC
      g_FiltrateUserName: TStringList;
      n4ADAE4: Integer;
      n4ADAE8: Integer;
      n4ADAEC: Integer;
      n4ADAF0: Integer;
      boDataDBReady: Boolean; //0x004ADAF4
      n4ADAFC: Integer;
      n4ADB00: Integer;
      n4ADB04: Integer;
      boHumDBReady: Boolean; //0x4ADB08
      n4ADBF4: Integer;
      n4ADBF8: Integer;
      n4ADBFC: Integer;
      n4ADC00: Integer;
      n4ADC04: Integer;
      boAutoClearDB: Boolean; //0x004ADC08
      g_nQueryChrCount: Integer; //0x004ADC0C
      nHackerNewChrCount: Integer; //0x004ADC10
      nHackerDelChrCount: Integer; //0x004ADC14
      nHackerSelChrCount: Integer; //0x004ADC18
      n4ADC1C: Integer;
      n4ADC20: Integer;
      n4ADC24: Integer;
      n4ADC28: Integer;
      n4ADC2C: Integer;
      n4ADB10: Integer;
      n4ADB14: Integer;
      n4ADB18: Integer;
      n4ADBB8: Integer;
      bo4ADB1C: Boolean;
      //以下是定义服务器设置变量
      sServerName: string = '新热血传奇';
      sConfFileName: string = '.\Dbsrc.ini';
      sConfClass: string = 'DBServer';
      sGateConfFileName: string = '.\!serverinfo.txt';
      sServerIPConfFileNmae: string = '.\!addrtable.txt';
      sFiltrateUserName: string = '.\FUserName.txt';
      sHeroDB: string = 'HeroDB';
      sMapFile: string;
      DenyChrNameList: TStringList;
      ServerIPList: TStringList;
      StdItemList: TList;
      MagicList: TList;
      g_SortMinLevel: Integer = 0;
      g_SortMaxLevel: Integer = 200;
      g_boAutoSort: Boolean = True;
      g_boSortClass: Boolean = False;
      g_btSortHour: Byte = 0;
      g_btSortMinute: Byte = 4;
      g_boArraySort: Boolean = False;
      g_boArraySortTime: LongWord;
      g_nClearRecordCount: Integer;
      g_nClearIndex: Integer; //0x324
      g_nClearCount: Integer; //0x328
      g_nClearItemIndexCount: Integer;
      boOpenDBBusy: Boolean; //0x350
      g_dwGameCenterHandle: THandle;
      g_boDynamicIPMode: Boolean = False;
      g_CheckCode: TCheckCode;
      g_ClearMakeIndex: TStringList;
      g_RouteInfo: array[0..19] of TRouteInfo;
      g_MainMsgList: TStringList;
      g_OutMessageCS: TRTLCriticalSection;
      ProcessHumanCriticalSection: TRTLCriticalSection;
      IDSocketConnected: Boolean;
      UserSocketClientConnected: Boolean;
      ServerSocketClientConnected: Boolean;
      DataManageSocketClientConnected: Boolean;
      ID_sRemoteAddress: string;
      User_sRemoteAddress: string;
      Server_sRemoteAddress: string;
      DataManage_sRemoteAddress: string;
      ID_nRemotePort: Integer;
      User_nRemotePort: Integer;
      Server_nRemotePort: Integer;
      DataManage_nRemotePort: Integer;
      dwKeepAliveTick: LongWord;
      dwKeepIDAliveTick: LongWord;
      dwKeepServerAliveTick: LongWord;
    
    const
      tDBServer = 0;
    
    implementation
    {实现部分不是很复杂,就不再注释了,不过有的涉及到之前提到的,还有其他单元引用的}
    
    uses
      DBSMain, HUtil32;
    
    procedure LoadIPTable();
    begin
      ServerIPList.Clear;
      try
        ServerIPList.LoadFromFile(sServerIPConfFileNmae);
      except
        MainOutMessage('加载IP列表文件 ' + sServerIPConfFileNmae + ' 出错!!!');
      end;
    end;
    
    function GetWaitMsgID(): Integer;
    begin
      Inc(g_nWaitMsgIndex);
      if g_nWaitMsgIndex <= 0 then
        g_nWaitMsgIndex := 1;
      Result := g_nWaitMsgIndex;
    end;
    
    procedure LoadConfig();
    var
      Conf: TIniFile;
    begin
      Conf := TIniFile.Create(sConfFileName);
      if Conf <> nil then
      begin
        sServerName := Conf.ReadString(sConfClass, 'ServerName', sServerName);
        nServerPort := Conf.ReadInteger(sConfClass, 'ServerPort', nServerPort);
        sServerAddr := Conf.ReadString(sConfClass, 'ServerAddr', sServerAddr);
        g_nGatePort := Conf.ReadInteger(sConfClass, 'GatePort', g_nGatePort);
        g_sGateAddr := Conf.ReadString(sConfClass, 'GateAddr', g_sGateAddr);
        sIDServerAddr := Conf.ReadString(sConfClass, 'IDSAddr', sIDServerAddr);
        nIDServerPort := Conf.ReadInteger(sConfClass, 'IDSPort', nIDServerPort);
        sHeroDB := Conf.ReadString(sConfClass, 'DBName', sHeroDB);
        sDataDBFilePath := Conf.ReadString(sConfClass, 'DBDir', sDataDBFilePath);
        g_boTestServer := not Conf.ReadBool(sConfClass, 'NotRepeatName', not g_boTestServer);
        g_boAutoSort := Conf.ReadBool(sConfClass, 'AutoSort', g_boAutoSort);
        g_boSortClass := Conf.ReadBool(sConfClass, 'SortClass', g_boSortClass);
        g_btSortHour := Conf.ReadInteger(sConfClass, 'SortHour', g_btSortHour);
        g_btSortMinute := Conf.ReadInteger(sConfClass, 'SortMinute', g_btSortMinute);
        g_SortMinLevel := Conf.ReadInteger(sConfClass, 'SortMinLevel', g_SortMinLevel);
        g_SortMaxLevel := Conf.ReadInteger(sConfClass, 'SortMaxLevel', g_SortMaxLevel);
    
        Conf.WriteString(sConfClass, 'ServerName', sServerName);
        Conf.WriteInteger(sConfClass, 'ServerPort', nServerPort);
        Conf.WriteString(sConfClass, 'ServerAddr', sServerAddr);
        Conf.WriteInteger(sConfClass, 'GatePort', g_nGatePort);
        Conf.WriteString(sConfClass, 'GateAddr', g_sGateAddr);
        Conf.WriteString(sConfClass, 'IDSAddr', sIDServerAddr);
        Conf.WriteInteger(sConfClass, 'IDSPort', nIDServerPort);
        Conf.WriteString(sConfClass, 'DBName', sHeroDB);
        Conf.WriteString(sConfClass, 'DBDir', sDataDBFilePath);
        Conf.WriteBool(sConfClass, 'AutoSort', g_boAutoSort);
        Conf.WriteBool(sConfClass, 'SortClass', g_boSortClass);
        Conf.WriteInteger(sConfClass, 'SortHour', g_btSortHour);
        Conf.WriteInteger(sConfClass, 'SortMinute', g_btSortMinute);
        Conf.WriteInteger(sConfClass, 'SortMinLevel', g_SortMinLevel);
        Conf.WriteInteger(sConfClass, 'SortMaxLevel', g_SortMaxLevel);
        Conf.WriteBool(sConfClass, 'NotRepeatName', not g_boTestServer);
        Conf.Free;
      end;
      LoadIPTable();
    end;
    
    function GetStdItemName(nPosition: Integer): string;
    var
      StdItem: pTStdItem;
    begin
      if (nPosition - 1 >= 0) and (nPosition < StdItemList.Count) then
      begin
        StdItem := StdItemList.Items[nPosition - 1];
        if StdItem <> nil then
        begin
          Result := StdItem.Name;
        end;
      end;
    end;
    
    function GetMagicName(wMagicId: Word): string;
    var
      i: Integer;
      Magic: pTMagic;
    begin
      for i := 0 to MagicList.Count - 1 do
      begin
        Magic := MagicList.Items[i];
        if Magic <> nil then
        begin
          if Magic.wMagicId = wMagicId then
          begin
            Result := Magic.sMagicName;
            break;
          end;
        end;
      end;
    end;
    
    function GetCodeMsgSize(X: Double): Integer;
    begin
      if INT(X) < X then
        Result := TRUNC(X) + 1
      else
        Result := TRUNC(X)
    end;
    
    function InClearMakeIndexList(nIndex: Integer): Boolean;
    var
      i: Integer;
    begin
      Result := False;
      for i := 0 to g_ClearMakeIndex.Count - 1 do
      begin
        if nIndex = Integer(g_ClearMakeIndex.Objects[i]) then
        begin
          Result := True;
          break;
        end;
      end;
    end;
    
    procedure MainOutMessage(sMsg: string);
    begin
      EnterCriticalSection(g_OutMessageCS);
      try
        g_MainMsgList.Add(sMsg);
      finally
        LeaveCriticalSection(g_OutMessageCS);
      end;
    end;
    
    procedure WriteLogMsg(sMsg: string);
    begin
    
    end;
    
    function CheckServerIP(sIP: string): Boolean;
    var
      i: Integer;
    begin
      Result := False;
      for i := 0 to ServerIPList.Count - 1 do
      begin
        if CompareText(sIP, ServerIPList.Strings[i]) = 0 then
        begin
          Result := True;
          break;
        end;
      end;
    end;
    
    procedure SendGameCenterMsg(wIdent: Word; sSendMsg: string);
    var
      SendData: TCopyDataStruct;
      nParam: Integer;
    begin
      nParam := MakeLong(Word(tDBServer), wIdent);
      SendData.cbData := Length(sSendMsg) + 1;
      GetMem(SendData.lpData, SendData.cbData);
      StrCopy(SendData.lpData, PChar(sSendMsg));
      SendMessage(g_dwGameCenterHandle, WM_COPYDATA, nParam, Cardinal(@SendData));
      FreeMem(SendData.lpData);
    end;
    
    function CheckFiltrateUserName(sName: string): Boolean;
    var
      i: integer;
    begin
      Result := False;
      for I := 0 to g_FiltrateUserName.Count - 1 do
      begin
        if AnsiContainsText(sName, g_FiltrateUserName.Strings[I]) then
        begin
          Result := True;
          break;
        end;
      end;
    end;
    
    constructor TGList.Create;
    begin
      inherited Create;
      InitializeCriticalSection(GLock);
    end;
    
    destructor TGList.Destroy;
    begin
      DeleteCriticalSection(GLock);
      inherited;
    end;
    
    procedure TGList.Lock;
    begin
      EnterCriticalSection(GLock);
    end;
    
    procedure TGList.UnLock;
    begin
      LeaveCriticalSection(GLock);
    end;
    
    procedure LoadFiltrateName();
    var
      i: Integer;
      TempList: TStringList;
      sStr: string;
    begin
      g_FiltrateUserName.Clear;
      TempList := TStringList.Create;
      TempList.Clear;
      try
        if FileExists(sFiltrateUserName) then
        begin
          TempList.LoadFromFile(sFiltrateUserName);
          for i := 0 to TempList.Count - 1 do
          begin
            sStr := TempList.Strings[I];
            if (Length(sStr) > 0) and (sStr[1] <> ';') then
              g_FiltrateUserName.Add(sStr);
          end;
        end
        else
        begin
          TempList.Add(';创建人物过滤字符,一行一个过滤');
          TempList.SaveToFile(sFiltrateUserName);
        end;
      finally
        TempList.Free;
      end;
    end;
    
    initialization
    begin
      InitializeCriticalSection(g_OutMessageCS);
      InitializeCriticalSection(HumDB_CS);
      g_MainMsgList := TStringList.Create;
      DenyChrNameList := TStringList.Create;
      ServerIPList := TStringList.Create;
      g_ClearMakeIndex := TStringList.Create;
      StdItemList := TList.Create;
      MagicList := TList.Create;
      g_FiltrateUserName := TStringList.Create;
    end;
    
    finalization
    begin
      DeleteCriticalSection(HumDB_CS);
      DeleteCriticalSection(g_OutMessageCS);
      DenyChrNameList.Free;
      ServerIPList.Free;
      g_ClearMakeIndex.Free;
      g_MainMsgList.Free;
      StdItemList.Free;
      MagicList.Free;
      g_FiltrateUserName.Free;
    end;
    
    end.

    接下来还有一个人物数据单元HUMDB.pas,需要先把之前的复习几遍才能去看,因为涉及到数据文件的读写,对于文件的学习需求马上就到来了,这些代码我都新建一个程序把它们一点一点敲进去编译一遍,然后再去看源代码的大概结构和关系,这样学习很费时间,但是我觉得比我一下子去学习若干基础性的东西要理解的快一点,当把整个服务端都初步过了一遍后,我会回头将记下来的需要巩固的基础性东西都重新练习即便,我发现,在写第一遍的时候是模棱两可,第二遍就不知不觉知道了某些对象和函数到底是干什么用的,第三遍的时候我大概能想到通过自己的方式去实现一些函数和过程,甚至可以增加和去掉某些不需要的结构变量,程序的功能正常运行,也许更改的东西不合理,但是锻炼了我的动手能力,对我的水平来说,光看一些优秀的代码我是学不到东西的,因为不动手,我看十几遍也不知道那到底要表达什么.

    我学习的时候一般都开两个DELPHI窗口,不知道有没有什么更好的办法,同时开两个代码提示就看不到了,不过这倒是提高了我的打字速度O(∩_∩)…

    关注过程,不知不觉就发现了结果原来如此...
  • 相关阅读:
    ExtJS小技巧
    Oracle 表的行数、表占用空间大小,列的非空行数、列占用空间大小 查询
    NPM 私服
    IDEA 不编译java以外的文件
    SQL 引号中的问号在PrepareStatement 中不被看作是占位符
    Chrome 浏览器自动填表呈现淡黄色解决
    批量删除Maven 仓库未下载成功.lastupdate 的文件
    Oracle 11g 监听很慢,由于监听日志文件太大引起的问题(Windows 下)
    Hibernate 自动更新表出错 建表或添加列,提示标识符无效
    Hibernate 自动更新表出错 More than one table found in namespace
  • 原文地址:https://www.cnblogs.com/iicc/p/5058927.html
Copyright © 2011-2022 走看看