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