zoukankan      html  css  js  c++  java
  • 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…,我不是业内之人,所以不知道代码越多,功能越多,就是感觉这些过程不如都写成通用的函数,然后通过传入服务结构变量实现服务的启动\监测\包括停止.

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

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

    关注过程,不知不觉就发现了结果原来如此...
  • 相关阅读:
    leetcode 3sum
    leetcode majority elements
    php调取linux的压缩命令进行压缩
    mysql 分区、分表、分库分表。
    如何在数据库中使用索引
    PDO 的基本操作
    JavaScript中常见的字符串操作函数及用法汇总
    php-fpm
    phpmyadmin导入导出数据库文件最大限制的解决方法
    php 获取今日、昨日、上周、本月的起始时间戳和结束时间戳。
  • 原文地址:https://www.cnblogs.com/iicc/p/5052058.html
Copyright © 2011-2022 走看看