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

一点废话:因为非工科出身,又对编程有点兴趣,杂乱的学习了好多(C,C++,PYTHON…)等好多语言,最后发现DEKLPI上手比较快,对于不知道线代和高数等是什么的我来说也许是较好的选择了,毕竟只是兴趣而已,对于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,效果如下:

服务端只有一个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);

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

时间: 2024-10-27 15:23:23

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

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)-04

接着学习,从学习的过程中,我发现了这个引擎控制台的主要功能,这也是一行一行代码敲进去的结果,之前我对这个单元的功能了解的还是少,不知不觉中就发现了它主要实现的功能,对里边的代码理解也进了一步. 从我的理解它大概有如下功能: a.实现整个服务端的启动配置. b.进行数据更新,这里指的是对数据库(人物\物品\怪物…)的更新. c.服务端初始化(清理数据和M2的变量复位) d.启动所有服务并监控其运行状态. e.备份数据. 除了d,其他的都还容易理解,基本上拖拉控件写上事件就可以完成,唯独对服务进程进

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游戏引擎——第五篇,人物&人物特效

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