DELPHI版传奇引擎学习菜鸟篇(applem2)-04

接着学习,从学习的过程中,我发现了这个引擎控制台的主要功能,这也是一行一行代码敲进去的结果,之前我对这个单元的功能了解的还是少,不知不觉中就发现了它主要实现的功能,对里边的代码理解也进了一步.

从我的理解它大概有如下功能:

a.实现整个服务端的启动配置.

b.进行数据更新,这里指的是对数据库(人物\物品\怪物…)的更新.

c.服务端初始化(清理数据和M2的变量复位)

d.启动所有服务并监控其运行状态.

e.备份数据.

除了d,其他的都还容易理解,基本上拖拉控件写上事件就可以完成,唯独对服务进程进行监控我一直找不到理解的方法,说明了我对WIN32API知道的太少了,对进程和线程的运行机理和消息处理机制还不明白,先用自己的话记下来,帮助理解,然后看API,了解处理方式.

从启动按钮点击,就可以发现一系列的调用,按照顺序如下:

procedure TfrmMain.ButtonStartGameClick(Sender: TObject);
begin
  SetWindowPos(Self.Handle, Self.Handle, Self.Left, Self.Top, Self.Width, Self.Height, $40);
  case m_nStartStatus of
    0:
      begin
        if Application.MessageBox(‘是否确认启动游戏服务器 ?‘, ‘确认信息‘, MB_YESNO + MB_ICONQUESTION) = mrYes then
        begin
          StartGame();  //调用启动过程
        end;
      end;
    1:
      begin
        if Application.MessageBox(‘是否确认中止启动游戏服务器 ?‘, ‘确认信息‘, MB_YESNO + MB_ICONQUESTION) = mrYes then
        begin
          TimerStartGame.Enabled := False;  //终止启动计时器
          m_nStartStatus := 2;
          ButtonStartGame.Caption := g_sButtonStopGame;
        end;
      end;
    2:
      begin
        if Application.MessageBox(‘是否确认停止游戏服务器 ?‘, ‘确认信息‘, MB_YESNO + MB_ICONQUESTION) = mrYes then
        begin
          StopGame();    //停止服务
        end;
      end;
    3:
      begin
        if Application.MessageBox(‘是否确认中止启动游戏服务器 ?‘, ‘确认信息‘, MB_YESNO + MB_ICONQUESTION) = mrYes then
        begin
          TimerStopGame.Enabled := False;    //终止停止计时器
          m_nStartStatus := 2;
          ButtonStartGame.Caption := g_sButtonStopGame;
        end;
      end;
  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;
  ....

  ButtonStartGame.Caption := g_sButtonStopStartGame;
  m_nStartStatus := 1;
  TimerStartGame.Enabled := True;//调用启动计时器按顺序启动所有服务
end;
procedure TfrmMain.TimerStartGameTimer(Sender: TObject);
var
  nRetCode: Integer;
  I: Integer;
  boStartRunGateOK: Boolean;
  wHour, wMin, wSec, wMSec: Word;
begin
  if DBServer.boGetStart then
  begin
    case DBServer.btStartStatus of
      0:
        begin
          nRetCode := RunProgram(DBServer, IntToStr(Self.Handle), 0);
          if nRetCode = 0 then
          begin
            DBServer.btStartStatus := 1;
            DBServer.ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, DBServer.ProcessInfo.dwProcessId);
            MainOutMessage(‘start DbServer:‘+IntToStr(DBServer.ProcessInfo.dwProcessId));//-----------------------------
          end
          else
          begin
            DBServer.btStartStatus := 2;
          end;
          Exit;
        end;
      1:
        begin
          Exit;
        end;
    end;
  end;
  TimerStartGame.Enabled := False;//启动成功,终止启动计时器
  TimerCheckRun.Enabled := True; //打开服务监测计时器,异常的服务会被重启
  ButtonStartGame.Caption := g_sButtonStopGame;
  m_nStartStatus := 2;//标志启动状态为2(已启动)
end;

上边的RunProgram(…)函数是调用外部程序的函数,对API我还需要加强学习,理解的很困难

function RunProgram(var ProgramInfo: TProgram; sHandle: string; dwWaitTime: LongWord): LongWord;
var
  StartupInfo: TStartupInfo; //获取进程状态
  sCommandLine: string;      //命令行参数
  sCurDirectory: string;     //程序目录
begin
  Result := 0;//执行外部程序,失败返回0,成功返回进程句柄
  FillChar(StartupInfo, SizeOf(TStartupInfo), #0);
  GetStartupInfo(StartupInfo);//获取进程的启动信息
  {设置命令行参数}
  sCommandLine := format(‘%s%s %s %d %d‘, [ProgramInfo.sDirectory, ProgramInfo.sProgramFile, sHandle, ProgramInfo.nMainFormX, ProgramInfo.nMainFormY]);
  sCurDirectory := ProgramInfo.sDirectory; //取得程序运行目录
  if not CreateProcess(nil,  //如果启动服务失败返回错误代码
    PChar(sCommandLine),
    nil,
    nil,
    True,
    0,
    nil,
    PChar(sCurDirectory),
    StartupInfo,
    ProgramInfo.ProcessInfo)
  then
  begin
    Result := GetLastError();
  end;
  Sleep(dwWaitTime);  //挂起
end;

服务启动之后就开始调用监测计时器监测服务状态:

procedure TfrmMain.TimerCheckRunTimer(Sender: TObject);
var
  dwExitCode: LongWord;
  nRetCode: Integer;
  I: Integer;
begin
  if DBServer.boGetStart then
  begin
    GetExitCodeProcess(DBServer.ProcessHandle, dwExitCode);
    MainOutMessage(‘check DbServer:‘+IntToStr(DBServer.ProcessHandle));
    //如果监测到服务没有启动则重新启动程序,这里没有实现代码重用
    if (dwExitCode <> STILL_ACTIVE) or (DBServer.ProcessHandle = 0) then
    begin
      nRetCode := RunProgram(DBServer, IntToStr(Self.Handle), 0);
      if nRetCode = 0 then
      begin
        CloseHandle(DBServer.ProcessHandle);
        DBServer.ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, DBServer.ProcessInfo.dwProcessId);
        if DBServer.MainFormHandle <> 0 then
          MainOutMessage(‘数据库异常关闭,已被重新启动...‘);
        DBServer.MainFormHandle := 0;
      end;
    end;
  end;
  ...
end;

因为所有的服务启动有一定的依赖性,所以考虑是不是可以通过传递进程间消息实现其他服务的启动,这样写一大堆相似的过程有点凑代码的嫌疑O(∩_∩)O…,我不是业内之人,所以不知道代码越多,功能越多,就是感觉这些过程不如都写成通用的函数,然后通过传入服务结构变量实现服务的启动\监测\包括停止.

终于发现了,这样学习有点绕远,因为好多基本的概念我还没有入门,但是我还是要坚持让自己慢慢消化这些,写一遍不懂就写两遍甚至多变,总会有一些收获的.

今天先学到这里,先把消息处理机制和进程线程通讯的概念搞清楚再继续下边的学习.

时间: 2024-10-19 14:16:39

DELPHI版传奇引擎学习菜鸟篇(applem2)-04的相关文章

DELPHI版传奇引擎学习菜鸟篇(applem2)

一点废话:因为非工科出身,又对编程有点兴趣,杂乱的学习了好多(C,C++,PYTHON…)等好多语言,最后发现DEKLPI上手比较快,对于不知道线代和高数等是什么的我来说也许是较好的选择了,毕竟只是兴趣而已,对于DELPHI的资料不是没有,就是觉得没有自己可以渐进入门的.因为以前玩过一个叫传奇的游戏,所以知道最早的传奇是DELPHI开发的,感觉还好,这就找了不少服务端学习(呵呵,研究说不上,因为咱没到那层次),自己动手架设修改,有时还提供给网友玩公益.然后就找传奇的DELPHI源码,不算很多,毕

DELPHI版传奇引擎学习菜鸟篇(applem2)-03

3.2 Gmain.pas单元 这是引擎控制台的主窗口,就是之前说的那个4500行代码的单元,对大神来说,这不算什么,对我看来说,光是理清里边的关系就很吃力.我知道也许从程序的架构角度去理解会好一些,但咱不懂那些,只好继续以一个菜鸟的方式按单元\按页面逐项查看,期待能获得一些营养. 接口部分也有近500行代码,虽然我是初学delphi,但也知道把所有的功能和定义都放到一个单元对以后维护带来很大不变,这份代码是谁写的我不清楚,但是至少不像比较标准的架构,除了重复的复制代码,就是一些嵌套的过程中的过

DELPHI版传奇引擎学习菜鸟篇(applem2)-05

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

DELPHI版传奇引擎学习菜鸟篇(applem2)-06

引擎源代码的学习暂时放下了几天,因为需要掌握一些进程处理方面的消息,之前在GAMECENTER中的启动服务过程好好琢磨了一下,将服务启动过程单独拿出来,原先是用主界面的过程判断处理启动,好长的代码,终于提炼出来了一个通用启动过程,停止服务的过程和这个是类似的. {运行外部程序的函数} function RunProgram(var ProgramInfo: TProgram; sHandle: string; dwWaitTime: LongWord): LongWord; var Startu

DELPHI版传奇引擎学习菜鸟篇(applem2)-02

每天只学习一个知识点,也是一种进步. 接着学习GShare.pas的实现部分,其实这个应该叫做GAMECENTER共享单元,我刚开始理解的是错误的,这是根据名字起的. 在学习实现部分之前,声明部分还有一些变量: //虽然光看这些变量不可能全部知道带表什么,但是为了学习,还是注释一下 var //下面4个应该是更新数据(格式)用的,默认为本机更新 g_sDataListAddrs: string = '127.0.0.1'; g_wDataListPort: Word = 18888; g_sDa

Xshell学习--菜鸟篇

1)关于Xshell 网上更多的资料里提到的SSH客户端是putty,因为简单.开源.免费.但是也正是由于功能过于简单,所以在这里推荐大家使用Xshell. Xshell最初并不能免费使用,而且也没有带中文语言,所以导致用户非常少,但是现在已经可以免费安装并且能设置中文,相信使用的人会越来越多. 简单说下个人认为Xshell的优点: 1,界面设计简洁,很人性化,用起来让人觉得很舒服. 2,支持标签,打开多个链接的时候很方便. 3,可以保存密码.(至于安全问题,见仁见智吧,个人觉得,如果能拿到保存

Docker虚拟化实战学习——基础篇(转)

Docker虚拟化实战学习--基础篇 2018年05月26日 02:17:24 北纬34度停留 阅读数:773更多 个人分类: Docker Docker虚拟化实战和企业案例演练 深入剖析虚拟化技术概念和应用场景 虚拟化,一是项技术--,是一种资源解决方案. 虚拟化技术是将物理资源转变为逻辑上可以管理的资源,以打破物理结构之间的壁垒,使计算元件运行在虚拟的基础上,而不是真实的物理资源上. 通过虚拟化技术,可以将物理资源转变为逻辑资源(虚拟机),应用程序服务运行在虚拟资源上,而不是真实的物理机上.

如何制作一款HTML5 RPG游戏引擎——第四篇,情景对话

今天我们来实现情景对话.这是一个重要的功能,没有它,游戏将变得索然无味.所以我们不得不来完成它. 但是要知道,使用对话可不是一件简单的事,因为它内部的东西很多,比如说人物头像,人物名称,对话内容... 因此我们只能通过数组+JSON来将对话信息装起来,然后根据信息作出不同的显示.接下来我便要向大家展示实现方法. 先看本系列文章目录: 如何制作一款HTML5 RPG游戏引擎--第一篇,地图类的实现 http://blog.csdn.net/yorhomwang/article/details/88

如何制作一款HTML5 RPG游戏引擎——第五篇,人物&amp;人物特效

上一次,我们实现了对话类,今天就来做一个游戏中必不可少的--人物类. 当然,你完全是可以自己写一个人物类,但是为了方便起见,还是决定把人物类封装到这个引擎里. 为了使这个类更有意义,我还给人物类加了几个特效,在以下讲解中会提到. 以下是本系列文章的目录: 如何制作一款HTML5 RPG游戏引擎--第一篇,地图类的实现 http://blog.csdn.net/yorhomwang/article/details/8892305 如何制作一款HTML5 RPG游戏引擎--第二篇,烟雨+飞雪效果 h