一點廢話:因為非工科出身,又對編程有點興趣,雜亂的學習了好多(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,效果如下:
//本身包含的單元 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);
先將學習過的溫習一下,然後再將服務器的狀態處理連貫做一下記錄.