3.2 Gmain.pas单元
这是引擎控制台的主窗口,就是之前说的那个4500行代码的单元,对大神来说,这不算什么,对我看来说,光是理清里边的关系就很吃力.我知道也许从程序的架构角度去理解会好一些,但咱不懂那些,只好继续以一个菜鸟的方式按单元\按页面逐项查看,期待能获得一些营养.
接口部分也有近500行代码,虽然我是初学delphi,但也知道把所有的功能和定义都放到一个单元对以后维护带来很大不变,这份代码是谁写的我不清楚,但是至少不像比较标准的架构,除了重复的复制代码,就是一些嵌套的过程中的过程和函数函数,这恰是我所需要的,让我明白了如何去架构一个良好的可维护的程序,以及很容易让别人读懂的程序,突然我想到了程序设计中面条式的复杂结构,至少能让我知道怎么去避免写一些不容易看懂的代码.
主单元的界面如下:
可以看出来这么多的界面集成到一个单元是多么的不容易,我觉得可以把界面封装到几个或几组dll里边,界面只实现与VCL状态有关的代码,应该好维护一点,500行声明有400行是界面的控件声明和过程声明,仔细过一遍发现很多过程实现都在窗体控件的方法里边,重用的可能性几乎为零.也许是早期代码的原因,但是有一点我不明白,delphi是很好的面向对象设计的IDE,怎么前辈们就没有想到或利用呢?开始怀疑这个源代码也许是高手写的初级版本,毕竟较成熟的版本很少拿来开源.
窗口自身的单元不用介绍,看界面就知道是干什么的,其定义的其他方法如下:
private m_boOpen: Boolean; //开关标志 m_nStartStatus: Integer;//启动状态标志,在GShare.pas里边有类似声明 m_dwShowTick: LongWord; m_dwRefTick: LongWord; {定义时间变量,用于启动脚本和备份脚本} m_btHour: Byte; m_btMinute: Byte; m_dwRunTime: LongWord; m_dwRunTick: LongWord; m_boGateStop: Boolean; m_boGateStopTick: LongWord; {上述几个适用于检测启动的服务的,异常或停止的通过设置状态重新启动} {下边是定义的私有过程,其实可以放到另一个单元} procedure RefGameConsole(); //加载配置信息到可视化界面 procedure GenGameConfig(); //写入游戏设置文件,以下几个都是 procedure GenDBServerConfig(); procedure GenLoginServerConfig(); procedure GenLogServerConfig(); procedure GenM2ServerConfig(); procedure GenLoginGateConfig(); procedure GenSelGateConfig(); procedure GenRunGateConfig; procedure GenPlugTopConfig(); {启动\停止服务,这里是通过设置状态,然后通过定时器启动,启动方法在GShare里边} procedure StartGame(); procedure StopGame(); {发送数据信息,还有一个MainOutMessage被我Ppublic了,为了调试用} procedure MainOutDataListMessage(sMsg: string); {发送进程消息,其实就是将服务的启动状态发送到引擎控制带主界面} procedure ProcessDBServerMsg(wIdent: Word; sData: string); procedure ProcessLoginSrvMsg(wIdent: Word; sData: string); procedure ProcessLogServerMsg(wIdent: Word; sData: string); procedure ProcessPlugTopMsg(wIdent: Word; sData: string); procedure ProcessLoginGateMsg(wIdent: Word; sData: string); procedure ProcessSelGateMsg(wIdent: Word; sData: string); procedure ProcessRunGateMsg(wIdent: Word; sData: string); function ProcessM2ServerMsg(wIdent: Word; sData: string): Integer; {加载RunGate网关的设置,因为不是一个,所以猜测Mut应该是多个的意思} procedure GetMutRunGateConfing(nIndex: Integer); function StartService(): Boolean;//这个是启动游戏控制台 {以下两个类似GetMutRunGateConfing,都是循环读取网关状态} procedure GenMutSelGateConfig(nIndex: Integer); procedure GenMutLoginGateConfig(nIndex: Integer); procedure LoadBackList();//加载备份列表 procedure RefBackListToView();//这个是将备份信息写到ListView里边 {这是通过Socket发送数据,带MD5校验} function SendDataListData(wIdent: Word; sData: string): Boolean; function RefDataListItem(): Boolean;//更新数据函数 procedure ClearDataListItem();//清除旧的数据列表 {下边几个是读取地图数据用的,暂时有点理解困难} function NewDataListInfo(): pTDataListInfo;//初始化新数据列表 procedure DisposeDataListInfo(DataListInfo: pTDataListInfo);//释放数据列表 function GetNextData(): Boolean; {取得地图数据信息} function MapGetFileData(sFileName: string; boBackData: Boolean): Integer; { Private declarations } public {处理消息用的两个过程} procedure ProcessMessage(var Msg: TMsg; var Handled: Boolean); procedure MyMessage(var MsgData: TWmCopyData); message WM_COPYDATA; procedure MainOutMessage(sMsg: string);//这个原先在private里边 { Public declarations } end;
实现的部分东西比较多,也比较乱,按顺序一个一个学习,RefGameConsole是重新加载所有配置,为引擎启动做准备.
procedure TfrmMain.RefGameConsole; begin m_boOpen := False; CheckBoxM2Server.Checked := g_Config.M2Server.GetStart; CheckBoxDBServer.Checked := g_Config.DBServer.GetStart; CheckBoxLoginServer.Checked := g_Config.LoginSrv.GetStart; CheckBoxLogServer.Checked := g_Config.LogServer.GetStart; CheckBoxLoginGate.Checked := g_Config.LoginGate.GetStart; CheckBoxSelGate.Checked := g_Config.SelGate.GetStart1; CheckBoxSelGate1.Checked := g_Config.SelGate.GetStart2; CheckBoxRunGate.Checked := g_Config.RunGate.GetStart[0]; CheckBoxRunGate1.Checked := g_Config.RunGate.GetStart[1]; CheckBoxRunGate2.Checked := g_Config.RunGate.GetStart[2]; CheckBoxRunGate3.Checked := g_Config.RunGate.GetStart[3]; CheckBoxRunGate4.Checked := g_Config.RunGate.GetStart[4]; CheckBoxRunGate5.Checked := g_Config.RunGate.GetStart[5]; CheckBoxRunGate6.Checked := g_Config.RunGate.GetStart[6]; CheckBoxRunGate7.Checked := g_Config.RunGate.GetStart[7]; EditGameDir.Text := g_sGameDirectory; EditHeroDB.Text := g_sHeroDBName; EditGameName.Text := g_sGameName; EditGameExtIPaddr.Text := g_sExtIPaddr; EditGameExtIPAddr2.Text := g_sExtIPAddr2; CheckBoxIP2.Checked := g_boIP2; CheckBoxCloseWuXin.Checked := g_boCloseWuXin; EditDBServer_MainFormX.Value := g_Config.DBServer.MainFormX; EditDBServer_MainFormY.Value := g_Config.DBServer.MainFormY; EditDBServerGatePort.Text := IntToStr(g_Config.DBServer.GatePort); EditDBServerServerPort.Text := IntToStr(g_Config.DBServer.ServerPort); CheckBoxDBServerGetStart.Checked := g_Config.DBServer.GetStart; EditLoginServer_MainFormX.Value := g_Config.LoginSrv.MainFormX; EditLoginServer_MainFormY.Value := g_Config.LoginSrv.MainFormY; EditLoginServerGatePort.Text := IntToStr(g_Config.LoginSrv.GatePort); EditLoginServerServerPort.Text := IntToStr(g_Config.LoginSrv.ServerPort); EditLoginServerMonPort.Text := IntToStr(g_Config.LoginSrv.MonPort); CheckBoxboLoginServer_GetStart.Checked := g_Config.LoginSrv.GetStart; EditM2Server_MainFormX.Value := g_Config.M2Server.MainFormX; EditM2Server_MainFormY.Value := g_Config.M2Server.MainFormY; EditM2ServerGatePort.Text := IntToStr(g_Config.M2Server.GatePort); EditM2ServerMsgSrvPort.Text := IntToStr(g_Config.M2Server.MsgSrvPort); CheckBoxM2ServerGetStart.Checked := g_Config.M2Server.GetStart; EditLogServer_MainFormX.Value := g_Config.LogServer.MainFormX; EditLogServer_MainFormY.Value := g_Config.LogServer.MainFormY; EditLogServerPort.Text := IntToStr(g_Config.LogServer.Port); CheckBoxLogServerGetStart.Checked := g_Config.LogServer.GetStart; CheckBoxboRunGate_GetStart1.Checked := g_Config.RunGate.GetStart[0]; CheckBoxboRunGate_GetStart2.Checked := g_Config.RunGate.GetStart[1]; CheckBoxboRunGate_GetStart3.Checked := g_Config.RunGate.GetStart[2]; CheckBoxboRunGate_GetStart4.Checked := g_Config.RunGate.GetStart[3]; CheckBoxboRunGate_GetStart5.Checked := g_Config.RunGate.GetStart[4]; CheckBoxboRunGate_GetStart6.Checked := g_Config.RunGate.GetStart[5]; CheckBoxboRunGate_GetStart7.Checked := g_Config.RunGate.GetStart[6]; CheckBoxboRunGate_GetStart8.Checked := g_Config.RunGate.GetStart[7]; EditRunGate_MainFormX.Value := g_Config.RunGate.MainFormX; EditRunGate_MainFormY.Value := g_Config.RunGate.MainFormY; EditRunGate_GatePort1.Text := IntToStr(g_Config.RunGate.GatePort[0]); EditRunGate_GatePort2.Text := IntToStr(g_Config.RunGate.GatePort[1]); EditRunGate_GatePort3.Text := IntToStr(g_Config.RunGate.GatePort[2]); EditRunGate_GatePort4.Text := IntToStr(g_Config.RunGate.GatePort[3]); EditRunGate_GatePort5.Text := IntToStr(g_Config.RunGate.GatePort[4]); EditRunGate_GatePort6.Text := IntToStr(g_Config.RunGate.GatePort[5]); EditRunGate_GatePort7.Text := IntToStr(g_Config.RunGate.GatePort[6]); EditRunGate_GatePort8.Text := IntToStr(g_Config.RunGate.GatePort[7]); EditSelGate_MainFormX.Value := g_Config.SelGate.MainFormX; EditSelGate_MainFormY.Value := g_Config.SelGate.MainFormY; CheckBoxboSelGate_GetStart.Checked := g_Config.SelGate.GetStart1; CheckBoxboSelGate_GetStart2.Checked := g_Config.SelGate.GetStart2; EditSelGate_GatePort.Text := IntToStr(g_Config.SelGate.GatePort[0]); EditSelGate_GatePort1.Text := IntToStr(g_Config.SelGate.GatePort[1]); EditLoginGate_MainFormX.Value := g_Config.LoginGate.MainFormX; EditLoginGate_MainFormY.Value := g_Config.LoginGate.MainFormY; CheckBoxboLoginGate_GetStart.Checked := g_Config.LoginGate.GetStart; EditLoginGate_GatePort.Text := IntToStr(g_Config.LoginGate.GatePort); CheckBoxPlugTopGetStart.Checked :=g_Config.PlugTop.GetStart; CheckBoxPlugTop.Checked:=g_Config.PlugTop.GetStart; EditPlugTop_MainFormX.Value:=g_Config.PlugTop.MainFormX; EditPlugTop_MainFormY.Value:=g_Config.PlugTop.MainFormY; m_boOpen := True; end;
看着上边好多类似的取值方式,让我想到了继承,可惜之前定义的都是结构,不然可以优化多,这个函数倒不用多做说明,太通俗易懂了.
下边几个过程是将设置写入服务配置文件,都是类似的,也不用多想就可以看懂,一共9个,第一个是调用其他8个的过程,就是主界面上保存设置按钮的事件.
procedure TfrmMain.GenGameConfig; begin GenDBServerConfig(); GenLoginServerConfig(); GenM2ServerConfig(); GenLogServerConfig(); GenRunGateConfig(); GenSelGateConfig(); GenLoginGateConfig(); GenPlugTopConfig(); end; procedure TfrmMain.GenDBServerConfig; var sIniFile: string; SaveList: TStringList; I: Integer; begin IfNoDirThenCreate(g_sGameDirectory + ‘DBServer\‘); IfNoDirThenCreate(g_sGameDirectory + ‘DBServer\DB\‘); WriteIniValue(‘Dbsrc.ini‘,DBSERVERSECTIONNAME2,‘ServerName‘,g_sGameName); WriteIniValue(‘Dbsrc.ini‘,DBSERVERSECTIONNAME2, ‘ServerAddr‘, sLocalIPaddr); WriteIniValue(‘Dbsrc.ini‘,DBSERVERSECTIONNAME2, ‘ServerPort‘, g_Config.DBServer.ServerPort); WriteIniValue(‘Dbsrc.ini‘,DBSERVERSECTIONNAME2, ‘GateAddr‘, sAllIPaddr); WriteIniValue(‘Dbsrc.ini‘,DBSERVERSECTIONNAME2, ‘GatePort‘, g_Config.DBServer.GatePort); WriteIniValue(‘Dbsrc.ini‘,DBSERVERSECTIONNAME2, ‘IDSAddr‘, sLocalIPaddr); WriteIniValue(‘Dbsrc.ini‘,DBSERVERSECTIONNAME2, ‘IDSPort‘, g_Config.LoginSrv.ServerPort); WriteIniValue(‘Dbsrc.ini‘,DBSERVERSECTIONNAME2, ‘DBName‘, g_sHeroDBName); WriteIniValue(‘Dbsrc.ini‘,DBSERVERSECTIONNAME2, ‘DBDir‘, ‘.\DB\‘); SaveList := TStringList.Create; SaveList.Add(sLocalIPaddr); if g_boIP2 then SaveList.Add(sLocalIPaddr2); SaveList.SaveToFile(sIniFile + ‘!addrtable.txt‘); SaveList.Clear; sIniFile := sLocalIPaddr; for I := Low(g_Config.RunGate.GetStart) to High(g_Config.RunGate.GetStart) do begin if g_Config.RunGate.GetStart[I] then sIniFile := sIniFile + Format(‘ %s %d‘, [g_sExtIPaddr, g_Config.RunGate.GatePort[I]]); end; SaveList.Add(sIniFile); if g_boIP2 then begin sIniFile := sLocalIPaddr2; for I := Low(g_Config.RunGate.GetStart) to High(g_Config.RunGate.GetStart) do begin if g_Config.RunGate.GetStart[I] then sIniFile := sIniFile + Format(‘ %s %d‘, [g_sExtIPaddr2, g_Config.RunGate.GatePort[I]]); end; SaveList.Add(sIniFile); end; SaveList.SaveToFile(g_sGameDirectory + ‘DBServer\!serverinfo.txt‘); SaveList.Free; if not FileExists(g_sGameDirectory + ‘DBServer\FUserName.txt‘) then begin SaveList := TStringList.Create; SaveList.Add(‘;创建人物过滤字符,一行一个过滤‘); SaveList.SaveToFile(g_sGameDirectory + ‘DBServer\FUserName.txt‘); SaveList.Free; end; end;
启动和停止服务的过程也不是太复杂,不过我看着怎么就那么"复杂",等待都过完一遍后要从头重新写一遍.
procedure TfrmMain.StartGame; var I: Integer; begin m_dwRunTick := GetTickCount; //返回系统启动经历的毫秒数 FillChar(DBServer, SizeOf(TProgram), #0); //用指定的值填充连续的字节为进程启动做准备 //读取服务启动前的初始化状态,以下类似 DBServer.boGetStart := g_Config.DBServer.GetStart; DBServer.boReStart := True; DBServer.sDirectory := g_sGameDirectory + ‘DBServer\‘; DBServer.sProgramFile := g_Config.DBServer.ProgramFile; DBServer.nMainFormX := g_Config.DBServer.MainFormX; DBServer.nMainFormY := g_Config.DBServer.MainFormY; FillChar(LoginServer, SizeOf(TProgram), #0); LoginServer.boGetStart := g_Config.LoginSrv.GetStart; LoginServer.boReStart := True; LoginServer.sDirectory := g_sGameDirectory + ‘LoginSrv\‘; LoginServer.sProgramFile := g_Config.LoginSrv.ProgramFile; LoginServer.nMainFormX := g_Config.LoginSrv.MainFormX; LoginServer.nMainFormY := g_Config.LoginSrv.MainFormY; FillChar(LogServer, SizeOf(TProgram), #0); LogServer.boGetStart := g_Config.LogServer.GetStart; LogServer.boReStart := True; LogServer.sDirectory := g_sGameDirectory + ‘LogServer\‘; LogServer.sProgramFile := g_Config.LogServer.ProgramFile; LogServer.nMainFormX := g_Config.LogServer.MainFormX; LogServer.nMainFormY := g_Config.LogServer.MainFormY; FillChar(M2Server, SizeOf(TProgram), #0); M2Server.boGetStart := g_Config.M2Server.GetStart; M2Server.boReStart := True; M2Server.sDirectory := g_sGameDirectory + ‘Mir200\‘; M2Server.sProgramFile := g_Config.M2Server.ProgramFile; M2Server.nMainFormX := g_Config.M2Server.MainFormX; M2Server.nMainFormY := g_Config.M2Server.MainFormY; //RunGate可以启动8个进程,所以用循环读取 FillChar(RunGate, SizeOf(RunGate), #0); for I := Low(RunGate) to High(RunGate) do begin RunGate[I].btStartStatus := 0; RunGate[I].boGetStart := g_Config.RunGate.GetStart[I]; RunGate[I].boReStart := True; RunGate[I].sDirectory := g_sGameDirectory + ‘RunGate\‘; RunGate[I].sProgramFile := g_Config.RunGate.ProgramFile; if ((I + 1) mod 2) = 0 then RunGate[I].nMainFormX := g_Config.RunGate.MainFormX + 276 else RunGate[I].nMainFormX := g_Config.RunGate.MainFormX; if I in [2, 3, 6, 7] then RunGate[I].nMainFormY := g_Config.RunGate.MainFormY + 187 else RunGate[I].nMainFormY := g_Config.RunGate.MainFormY; //187 end; FillChar(SelGate, SizeOf(TProgram), #0); SelGate.boGetStart := g_Config.SelGate.GetStart1; SelGate.boReStart := True; SelGate.sDirectory := g_sGameDirectory + ‘SelGate\‘; SelGate.sProgramFile := g_Config.SelGate.ProgramFile; SelGate.nMainFormX := g_Config.SelGate.MainFormX; SelGate.nMainFormY := g_Config.SelGate.MainFormY; FillChar(SelGate1, SizeOf(TProgram), #0); SelGate1.boGetStart := g_Config.SelGate.GetStart2; SelGate1.boReStart := True; SelGate1.sDirectory := g_sGameDirectory + ‘SelGate\‘; SelGate1.sProgramFile := g_Config.SelGate.ProgramFile; SelGate1.nMainFormX := g_Config.SelGate.MainFormX; SelGate1.nMainFormY := g_Config.SelGate.MainFormY; FillChar(LoginGate, SizeOf(TProgram), #0); LoginGate.boGetStart := g_Config.LoginGate.GetStart; LoginGate.boReStart := True; LoginGate.sDirectory := g_sGameDirectory + ‘LoginGate\‘; LoginGate.sProgramFile := g_Config.LoginGate.ProgramFile; LoginGate.nMainFormX := g_Config.LoginGate.MainFormX; LoginGate.nMainFormY := g_Config.LoginGate.MainFormY; FillChar(LoginGate2, SizeOf(TProgram), #0); LoginGate2.boGetStart := g_Config.LoginGate.GetStart and g_boIP2; LoginGate2.boReStart := True; LoginGate2.sDirectory := g_sGameDirectory + ‘LoginGate\‘; LoginGate2.sProgramFile := g_Config.LoginGate.ProgramFile; LoginGate2.nMainFormX := g_Config.LoginGate.MainFormX; LoginGate2.nMainFormY := g_Config.LoginGate.MainFormY; FillChar(PlugTop, SizeOf(TProgram), #0); PlugTop.boGetStart := g_Config.PlugTop.GetStart; PlugTop.boReStart := True; PlugTop.sDirectory := g_sGameDirectory + ‘Mir200\‘; PlugTop.sProgramFile := g_Config.PlugTop.ProgramFile; PlugTop.nMainFormX := g_Config.PlugTop.MainFormX; PlugTop.nMainFormY := g_Config.PlugTop.MainFormY; CheckDBServer.Checked := DBServer.boGetStart; CheckLoginSrv.Checked := LoginServer.boGetStart; CheckM2Server.Checked := M2Server.boGetStart; CheckLogServer.Checked := LogServer.boGetStart; CheckPlugTop.Checked := PlugTop.boGetStart; CheckLoginGate.Checked := LoginGate.boGetStart; CheckSelGate1.Checked := SelGate.boGetStart; CheckSelGate2.Checked := SelGate1.boGetStart; CheckRunGate1.Checked := RunGate[0].boGetStart; CheckRunGate2.Checked := RunGate[1].boGetStart; CheckRunGate3.Checked := RunGate[2].boGetStart; CheckRunGate4.Checked := RunGate[3].boGetStart; CheckRunGate5.Checked := RunGate[4].boGetStart; CheckRunGate6.Checked := RunGate[5].boGetStart; CheckRunGate7.Checked := RunGate[6].boGetStart; CheckRunGate8.Checked := RunGate[7].boGetStart; ButtonStartGame.Caption := g_sButtonStopStartGame; m_nStartStatus := 1; TimerStartGame.Enabled := True;//调用启动计时器按顺序启动所有服务 end; procedure TfrmMain.StopGame; begin ButtonStartGame.Caption := g_sButtonStopStopGame; MainOutMessage(‘正在开始停止服务器...‘); TimerCheckRun.Enabled := False; //停止服务监测 TimerStopGame.Enabled := True; //调用停止计时器按启动的相反顺序停止服务 m_boGateStop := False; m_nStartStatus := 3; end;
接下来的学习就涉及到了服务启动配置文件和启动设置问题,用到的win API较多,尤其是进程的通讯和消息处理,一个函数需要反复调试多遍我才能理解出一点东西,其中的穿插调用也很频繁,自己先将之前的复习一遍,为后边的学习做准备.