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(∩_∩)…
关注过程,不知不觉就发现了结果原来如此...
|