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

http://www.cnblogs.com/iicc/p/5042667.html

点废话:因为非工科出身,又对编程有点兴趣,杂乱的学习了好多(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,效果如下:

360截图20151213130625731

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

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

关注过程,不知不觉就发现了结果原来如此...
相关推荐
百度云盘分享 简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!此时此景,笔者只专注Android、Iphone等移动平台开发,看着这些源码心中有万分感慨,写此文章纪念那时那景! Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除,从账户中取出amt,如果amt>账户余额抛出异常,一个实体Bean可以表示不同的数据实例,我们应该通过主键来判断删除哪个数据实例…… ejbCreate函数用于初始化一个EJB实例 5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口函数得到远程接口的引用,用远程接口的引用访问EJB。 EJB中JNDI的使用源码例子 1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户可以在终端上直接地使用它,但是它的主要作用是供程序使用的。本规范尝试满足大型主机、微型主机、个人工作站、和TACs 的不同需求。例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天通信演示代码 2个目标文件,一个服务器,一个客户端。 Java Telnet客户端实例源码 一个目标文件,演示Socket的使用。 Java 组播组中发送和接受数据实例 3个目标文件。 Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密   Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。   设定字符串为“张三,你好,我是李四”   产生张三的密钥对(keyPairZhang)   张三生成公钥(publicKeyZhang)并发送给李四,这里发送的是公钥的数组字节   通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张三编码后的公钥,将其解码,李四用张三的公钥加密信息,并发送给李四,张三用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 同上 java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥   Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从文件中得到公钥编码的字节数组、如何从字节数组解码公钥。 Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输   Jav
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页