一点废话:因为非工科出身,又对编程有点兴趣,杂乱的学习了好多(C,C++,PYTHON…)等好多语言,最后发现DELPHI上手比较快,对于不知道线代和高数等是什么的我来说也许是较好的选择了,毕竟只是兴趣而已,对于DELPHI的资料不是没有,就是觉得没有自己可以渐进入门的.因为以前玩过一个叫传奇的游戏,所以知道最早的传奇是DELPHI开发的,感觉还好,这就找了不少服务端学习(呵呵,研究说不上,因为咱没到那层次),自己动手架设修改,有时还提供给网友玩公益.然后就找传奇的DELPHI源码,不算很多,毕竟现在大部分还是用C写的,也不会开源给大家看,后来听网友说APPLEM2引擎不知道因为什么开源了,就找到一个比较全的(当然还是少了很多东西,比如三方VLC),看着前辈们写的东西,在佩服的同时也觉得自己对照写一遍也许会得到点什么,于是就开始了这个过程,决心从头开始把代码都敲一遍,尽管时间很长,也有点"盗用"(虽说APPLEM2开源了,但是架构模式也还是有知识产权的)嫌疑,呵呵…,不管怎么说,我发现这样学习东西很快,至少对我来说是这样的,这样的过程让我知道了什么是记录,什么是类,什么是SOCKET,对于初学的我来说,收获还是颇丰的,当然也发现了早期代码有不少的不妥之处,函数和过程的繁杂让我一贯找不着北,在试着将一些繁杂的代码简化后,突然发现有的函数被我重新写过了,也许这也是一种提高的方式吧,希望我能坚持下去.正题开始.

后续所有内容都是我自己学习DELPHI过程中对程序设计的一点浅显的了解,有的也许会引发前辈门笑喷,但是这恰是我学习的成长过程,也是我提高的途径.

 

1.传奇服务端结构:

大部分名字都叫MirServer,基本结构包含八个文件夹和一个GAMECENTER.EXE文件和一个CONFIG.INI文件,以下按照启动顺序说明.

名称

说明

描述

GAMECENTER

控制中心

引导所有服务端程序启动,早期的端我没看到过

DBServer

数据库服务器

管理人物\怪物\物品\魔法数据

LoginSrv

登录服务器

控制账号登录

LogServer

日志服务器

记录玩家操作日志

Mir200

游戏主引擎

管理游戏庞大的脚本和设置

RunGate

游戏网关

呵呵,现在我还不知道什么是网关

SelGate

角色网关

好像进入游戏与角色选择有关吧

LoginGate

登录网关

好像是登录控制和玩家状态检测的

Mud2

数据文件夹

物品\魔法\怪物数据,应该是paradox的

applem2的还有个排行榜的,我想大部分端应该集成在M2里边了吧.

能够看到的就这些,既然从头开始,就先把目录硬记下来,虽然后边在"抄写"的过程中会改变一些设置.

2.源代码结构

源代码和上述结构一样,除了MUD2,每个都对应一个工程文件,用了好多插件,准备把不需要的都去掉,把所有的服务端程序集中到一个进程里边,暂时不考虑性能如何,尽量用不带插件的DELPHI完整编译,版本以2007为基准吧.先写完了一个GAMECENTER和DBServer,效果如下:

传奇 Geem2 尾端 传奇2引擎_服务端

服务端只有一个Server.exe程序,其他服务都集成到一个父窗口中,然后根据主程序设置决定需要启动那些服务,毕竟传奇架设的时候有些服务可能不在一个服务器上,虽然是菜鸟,但是咱也得考虑远一点,学习前辈们的一些先进理念,也是拓展了自己的学习思路.

3.GAMECENTER

先说说启动中心,不说别的,光是主窗口4000多行的代码就让我眼花缭乱了,这对我来说太难了,有的过程或函数快300行了,阅读比较困难,还是先从自己的角度去理解吧.

GAMECENTER工程架构如下(名字都是从自己理解的角度起的):

//本身包含的单元
ugamecenter.pas     // 主窗口单元
GShare.pas          // 全局常量单元
DataBackUp.pas      // 数据备份单元
//引用的单元
DBShare.pas         // 共享数据单元
HUtil32.pas         // 人物操作单元
MD5Unit.pas         // 数据校验单元
Common.pas          // 通用常量单元

3.1 GShare.pas单元

单元之间的引用很复杂,也许是因为早起代码的原因吧,先说说GShare.pas单元,这个单元包含了服务器的配置常量,如文件夹名字\服务状态\配置文件等全局常量和服务的启动\停止函数以及消息处理过程,先记录一点自己能够理解的.

unit GShare;

interface
uses
  Windows, Messages, Classes, SysUtils, INIFiles, DataBackUp, ComCtrls;

const
  MAXRUNGATECOUNT = 8;  // 最大游戏网关数量
  {以下0-9是每个服务的消息编号常量}
  tDBServer = 0;
  tLoginSrv = 1;
  tLogServer = 2;
  tM2Server = 3;
  tLoginGate = 4;
  tSelGate = 6;
  tRunGate = 8;
  tPlugTop = 9;
  {不言而喻,这里是服务配置INI文件的节名称常量}
  BasicSectionName = 'GameConfig';
  DBServerSectionName = 'DBServer';
  LoginSrvSectionName = 'LoginSrv';
  M2ServerSectionName = 'M2Server';
  LogServerSectionName = 'LogServer';
  RunGateSectionName = 'RunGate';
  SelGateSectionName = 'SelGate';
  LoginGateSectionName = 'LoginGate';
  PlugTopSectionName='PlugTop';
  {IP设置,APPLEM2自带一机双IP设置}
  sAllIPaddr = '0.0.0.0';
  sLocalIPaddr = '127.0.0.1';
  sLocalIPaddr2 = '127.0.0.2';
  nLimitOnlineUser = 2000;    //服务器最高上线人数(源码自带注释)
  {以下是各个服务的配置路径和文件常量}
  SERVERCONFIGDIR = 'Config\';
  SERVERCONFIGFILE = 'Config.ini';
  SERVERGAMEDATADIR = 'GameData\';
  SERVERLOGDIR = 'Log\';

  DBSERVERSECTIONNAME2 = 'DBServer';
  DBSERVERDBDIR = 'DB\';
  DBSERVERALLOWADDR = 'AllowAddr.txt';
  DBSERVERGATEINFO = 'GateInfo.txt';

  LOGINSRVSECTIONNAME2 = 'LoginSrv';
  LOGINSRVCHRLOGNAME = SERVERLOGDIR + 'ChrLog\';
  LOGINSRVALLOWADDR = 'LoginSrv_AllowAddr.txt';
  LOGINSRVGETINFO = 'LoginSrv_GateInfo.txt';
  LOGINSRVUSERLIMIT = 'LoginSrv_UserLimit.txt';

  M2SERVERCONFIGFILE = '!Setup.txt';
  M2SERVERSECTIONNAME1 = 'Server';
  M2SERVERSECTIONNAME2 = 'Share';
  M2SERVERSEGuildBase = SERVERGAMEDATADIR + 'GuildBase\';
  M2SERVERSEGuildDir = M2SERVERSEGuildBase + 'Guilds\';
  M2SERVERSEGuildFile = M2SERVERSEGuildBase + 'GuildList.txt';
  M2SERVERSEConLogDir = SERVERLOGDIR + 'M2ConLog\';
  M2SERVERSECastleDir = SERVERGAMEDATADIR + 'Castle\';
  M2SERVERSECastleFile = SERVERGAMEDATADIR + 'Castle\List.txt';
  M2SERVERSELogDir = SERVERLOGDIR + 'M2Log\';
  M2SERVERSEEMailDir = SERVERLOGDIR + 'M2Log\';
  M2SERVERSEnvirDir = 'Envir\';
  M2SERVERSMapDir = 'Map\';
  M2SERVERSALLOWADDR = 'M2Server_AllowAddr.txt';
  M2SERVERSEmailDir = SERVERGAMEDATADIR + 'EMail\';

  LOGSERVERSECTIONNAME2 = 'LogDataServer';
  LOGSERVERBaseDir = SERVERGAMEDATADIR + 'GameLog\';
  RunGateSectionName2 = 'RunGate';
  SelGateSectionName2 = 'SelGate';
  LoginGateSectionName2 = 'LoginGate';
  PlugTopDIR=SERVERGAMEDATADIR +'\mir200\';

type
  {定义每个服务的应用程序状态结构指针}
  pTProgram = ^TProgram;
  TProgram = packed record
    boGetStart: Boolean; //DBServer启动标志 (源码自带注释)
    boReStart: Boolean; //程序异常停止,是否重新启动 (源码自带注释)
    btStartStatus: Byte;//0,1,2,3 未启动,正在启动,已启动,正在关闭 (源码自带注释)
    sProgramFile: string[50];
    sDirectory: string[100];
    ProcessInfo: TProcessInformation; //服务的进程信息(进程,线程,进程ID,线程ID)
    ProcessHandle: THandle;           //进程句柄
    MainFormHandle: THandle;          //主窗口句柄,后续改为每个服务对应的活动窗口句柄
    nMainFormX: Integer;  //服务端启动后窗口位置
    nMainFormY: Integer;
  end;
  {应该是加载地图文件的结构指针}
  pTDataListInfo = ^TDataListInfo;
  TDataListInfo = packed record
    sFileName: string[255];
    MapFileHandle: THandle;
    MapFileBuffer: PChar;
    DateTime: TDateTime;
    Data: PChar;
    DataSize: Integer;
    Item: TListItem;
  end;
  {检测服务运行状态}
  TCheckCode = packed record
    dwThread0: LongWord;
    sThread0: string;
  end;
  {下边的一堆CONFIG是对应的每个服务状态的结构}
  TDBServerConfig = packed record
    MainFormX: Integer;
    MainFormY: Integer;
    GatePort: Integer;
    ServerPort: Integer;
    GetStart: Boolean;
    ProgramFile: string[50];
  end;

  TLoginSrvConfig = packed record
    MainFormX: Integer;
    MainFormY: Integer;
    GatePort: Integer;
    ServerPort: Integer;
    MonPort: Integer;
    GetStart: Boolean;
    ProgramFile: string[50];
  end;

  TM2ServerConfig = packed record
    MainFormX: Integer;
    MainFormY: Integer;
    GatePort: Integer;
    MsgSrvPort: Integer;
    GetStart: Boolean;
    ProgramFile: string[50];
  end;

  TLogServerConfig = packed record
    MainFormX: Integer;
    MainFormY: Integer;
    Port: Integer;
    GetStart: Boolean;
    ProgramFile: string[50];
  end;

    TPlugTopConfig = packed record
    MainFormX: Integer;
    MainFormY: Integer;
    Port: Integer;
    GetStart: Boolean;
    ProgramFile: string[50];
  end;

  TRunGateConfig = packed record
    MainFormX: Integer;
    MainFormY: Integer;
    GetStart: array[0..MAXRUNGATECOUNT - 1] of Boolean;
    GatePort: array[0..MAXRUNGATECOUNT - 1] of Integer;
    ProgramFile: string[50];
  end;

  TSelGateConfig = packed record
    MainFormX: Integer;
    MainFormY: Integer;
    GatePort: array[0..1] of Integer;
    GetStart1: Boolean;
    GetStart2: Boolean;
    ProgramFile: string[50];
  end;

  TLoginGateConfig = packed record
    MainFormX: Integer;
    MainFormY: Integer;
    GatePort: Integer;
    GetStart: Boolean;
    ProgramFile: string[50];
  end;
  {将所有的服务状态声明为一个结构指针}
  pTConfig = ^TConfig;
  TConfig = packed record
    DBServer: TDBServerConfig;
    LoginSrv: TLoginSrvConfig;
    M2Server: TM2ServerConfig;
    LogServer: TLogServerConfig;
    RunGate: TRunGateConfig;
    SelGate: TSelGateConfig;
    LoginGate: TLoginGateConfig;
    PlugTop: TPlugTopConfig;
  end;

  procedure LoadConfig();  //加载启动设置
  procedure SaveConfig();  //保存启动设置
  {下边2个是启动和停止每个服务的函数}
  function RunProgram(var ProgramInfo: TProgram; sHandle: string; dwWaitTime: LongWord): LongWord;
  function StopProgram(var ProgramInfo: TProgram; dwWaitTime: LongWord): Integer;
  {发送每个服务当前状态的消息处理过程}
  procedure SendProgramMsg(DesForm: THandle; wIdent: Word; sSendMsg: string);

先将学习过的温习一下,然后再将服务器的状态处理连贯做一下记录.

关注过程,不知不觉就发现了结果原来如此...