概述
转自:陈波的博客
原文:http://blog.163.com/cp7618@yeah/blog/static/702347772010111434342665/
交叉阅读:
MaNGOS之验证Realm登录服务器
Mangos之SMSG_COMPRESSED_UPDATE_O…
Mangos之异步数据库查询
Mangos服务器会话WorldSession
Mangos之Socket处理方式
reamld中socket的处理方式
Mangosd消息的对称加密解析
详解验证Mangos服务器:消息SMSG_A…
Mangos游戏世界主线程解析
逻辑层: 类World实现了wow的World,所有的逻辑处理
MaNGOS 下载,编译,配置和运行的基本步骤 下载和安装msysgit,用于代码管理我使用的是Git-1.6.5.1-preview20091022.exe
下载和安装tortoisegit,用于代码管理我使用的是TortoiseGit-1.3.2.0-32bit.msi
使用git://github.com/mangos/mangos.git,从github提取mangos代码
采用Git GUI工具的Clone Existing Repository,得到 mangos代码(我的是9560,安装UDB参考这)
使用git://github.com/scriptdev2/scriptdev2.git,从github提取scriptdev2代码
采用Git GUI工具的Clone Existing Repository,得到 scriptdev2代码.
编译mangos我用的是VC9,打开mangoswinmangosdVC90.sln进行构造,构造完成后,会得到mangosbinWin32_Debug文件夹
编译scriptdev2我用的是VC9,打开mangoswinscriptdev2VC90.sln进行构造,构造完成后,会得到scriptdev2binWin32_Debug文件夹也可参考
pandore
拷贝mangossrcmangosd目录下的mangosd.conf.dist.in为mangosbinWin32_Debugmangosd.conf
拷贝mangossrcrealmd目录下的realmd.conf.dist.in为mangosbinWin32_Debugrealmd.conf
从http://www.wowtaiwan.com.tw/下载和安装台服WOW,并升级到最新版本我使用的是台服WOW 3.3.2 build 11403
,采用MaNGOS的工具ad从wow的MPQ中抽取map,得到的所有的map数据文件,文件命名规范为map_id(3位) tileY(2位) tileX(2位).map,如文件名为0002035.map,代表的是Azeroth(地图id为000,tile坐标为(35,20). 注:WOW客户端的Tile对应mangos中的grid,WOW客户端的Chunk对应mangos中的cell(1cell = 4 chunk)
按照常规流程(包括建立数据库和配置服务器)把服务器跑起来,使用account create zzh1234567 zzh1234567 创建一个账号,使用account set gmlevel zzh1234567 3设置为超级用户
配置好客户端后,运行WOW,顺利登陆,呵呵
通过安装UDB来丰富场景FULL DB 9560 : HERE,参考这,在我安装DB9560的时候,发现Mangos在LoadCreatureAddons的时候,加载creature template addons出错,Mangos只要求creature template addons有7个字段,而creature template addons有9个字段我现在只是简单地跳过LoadCreatureAddons的调用
运行Mangos (运行Mangos一节过期,是以前我针对国服3.1.3版本进行的安装配置) ,采用MaNGOS的工具ad从wow的MPQ中抽取map,得到的所有的map数据文件,文件命名规范为map_id(3位) tileY(2位) tileX(2位).map,如文件名为0002035.map,代表的是Azeroth(地图id为000,tile坐标为(35,20). 注:WOW客户端的Tile对应mangos中的grid,WOW客户端的Chunk对应mangos中的cell(1cell = 4 chunk)
配置好客户端后,运行WOW,顺利登陆,呵呵
Mangos代码阅读
Mangos有13个工程
使用了4个外部工具库,分别是: 跨平台的网络通讯框架The ADAPTIVE Communication Environment (ACE)
压缩库zlib
Socket通信库 C++ Sockets Library (使用在realmd工程中,和使用在Mangosd工程中的RASocket,负责处理Remote Administration其他地方没有使用到这个C++ Sockets Library )发现在C++ Sockets Library的TcpSocket::Open中存在一个问题,在n = connect(s, ad, ad);语句执行后,如果n=-1,C++ Sockets Library会检测是否ERR为WSAEWOULDBLOCK,否则表示成功,但在动态库中使用TcpSocket的时候,我发现n = connect(s, ad, ad);语句执行后,n=-1,ERR会为0,这个时候连接也是成功了,但TcpSocket::Open会当做不成功处理我发现这个问题,但没有时间去探究原因,也许并不是一个问题
C++的并行编程模板库Threading Building Blocks (tbb 和 tbbmalloc)
Mangos的实现分为:登录服务器(realmd)和世界服务器(mangosd+game)realmd和mangos共用了Mangos公共库(shared)
工程shared
提供了通用功能,包括了数据库的封装类,实现了对MySql的访问,同样,我们可以编写派生类来支持其他的数据库
工程script
提供了脚本接口,并实现了简单的几个脚本,封装为DLL,提供给game使用,具体可参考:MaNGOS脚本接口
通过使用不同的脚本DLL来替换share的这个DLL,可以让game具有更强的AIScriptDev2 就是一个这样的库ScriptDev2 is a replacement for the Script Library that comes with MaNGOS( http://www.getmangos.com ) written in C++ and is compatible with Windows and Linux. It provides scripts for NPCs, Boss events, and Items currently. On
工程mangosd
mangos是世界服务器的管理器,负责初始化工作和启动世界服务器各层的线程,这些工作主要是由类Master来实现具体是: 使用三个数据库对象WorldDatabase和CharacterDatabase和loginDatabase,初始化三大数据库:World Database和Character Database和login Database,并为每个数据库的访问都启动一个DB delay threads具体的数据库操作功能都是由Mangos公共库shared来提供
调用sWorld.SetInitialWorldSettings,对World进行初始化,包括加载所有的游戏数据和初始化各种更新定时器和邮件定时器,还有些其他的初始化工作类World的成员函数SetInitialWorldSettings调用成员函数LoadConfigSettings解析mangosd.conf,解析后内容放入uint32 m_configs[CONFIG_VALUE_COUNT]中
加载的游戏数据有:
DBC数据
Objects数据
Spells数据
Pooling数据
Game Event数据
loot数据
技能数据
所有其他的游戏数据,包括Waypoints和Trainers等等等
脚本数据
其他的初始化工作有:
初始化MapManager,启动Map System
初始化Battlegrounds,启动BattleGround System
初始化DailyQuestResetTime
初始化sGameEventMgr,Starting Game Event system
类Master启动WorldRunnable,开始游戏逻辑Heartbeat for the World,由Master创建,并设置线程为最高优先级
类Master启动CliRunnable:Command Line Interface handling thread,由Master创建CliRunnable运行时候会生成一个WorldDatabase线程,在接收到输入后会调用sWorld.QueueCliCommand把Cmd放入到World::cliCmdQueue中
mangosd的线程总共有(1+3+1+1+1+2 +1 =10)10个线程 主线程Master
2个网络线程ReactorRunnable(可配置数目)(网络层)
一个World线程(逻辑层)
三个DB线程(数据层)
一个CLI线程(输入层),运行时候会生成一个WorldDatabase线程
一个RA线程(管理层)
一个freeze catcher 线程(可选)
工程g3dlite:游戏逻辑层的底层库
工程framework:系统框架
工程realm
负责登陆和选择游戏服务器,进行负载均衡用到了C++ Sockets Library进行登录处理,采用select I/O模型实现了Wow, Mangos登录时的SRP6认证客户端作为它的client连接到realm server认证和选择了mangos server就断开 而mangos server和realm server则不进行连接,只是通过数据库交互数据:mangos server把自己的状态和拥有的角色数放入库中realm server会读取数据库中的这些信息来获知mangos server的状态 数据库realm的realmlist表保存了realm的列表
realm通过如下事件处理函数来负责登陆和选择游戏服务器
const AuthHandler table[] =
{
{ AUTH_LOGON_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleLogonChallenge },
{ AUTH_LOGON_PROOF, STATUS_CONNECTED, &AuthSocket::_HandleLogonProof },
{ AUTH_RECONNECT_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleReconnectChallenge},
{ AUTH_RECONNECT_PROOF, STATUS_CONNECTED, &AuthSocket::_HandleReconnectProof },
{ REALM_LIST, STATUS_AUTHED, &AuthSocket::_HandleRealmList },
{ XFER_ACCEPT, STATUS_CONNECTED, &AuthSocket::_HandleXferAccept },
{ XFER_RESUME, STATUS_CONNECTED, &AuthSocket::_HandleXferResume },
{ XFER_CANCEL, STATUS_CONNECTED, &AuthSocket::_HandleXferCancel }
}; 登录处理:
user登录到realm server进行身份认证,并选择登录上哪个mangos serveruser登录到mangos server后,将不再和realm server交互
参考: Wow 服务器解析
工程game
game:是Mangos的核心代码,网络层和逻辑层代码(采用了ACE反应器(Reactor)模式)
网络层: WorldSocket :负责网络IO,而类WorldSession负责逻辑处理WorldSocket和WorldSession分别在独立的线程ReactorRunnable和WorldRunnable中运行,使用WorldSession中的消息队列_recvQueue来进行数据缓冲在WorldSocket接收到网咯输入后,会调用m_Session->QueuePacket (new_pct);把网络包放入WorldSession的_recvQueue所以,可以看到WorldSocket 是Mangos game的网络层,而WorldSession是逻辑处理层WorldSocketMgr是网络层的一个管理器,它负责指派WorldSocket归哪个ReactorRunnable管理(Mangos可创建多个ReactorRunnable,缺省是2个)
WorldSocketMgr(Manages all sockets connected to peers and network threads)管理所有的连接WorldSocketWorldSocketMgr的WorldSocketMgr::StartNetwork对8085(缺省)端口进行侦听逻辑处理的循环是在World::Update中循环处理包括:
刷新更新定时器
刷新游戏定时器和处理游戏关闭
处理日常任务
处理拍卖
刷新SessionsWorld::UpdateSessions会调用所有WorldSession的WorldSession::Update在WorldSession::Update中进行逻辑处理
处理天气
刷新uptime table
刷新Objects,包括maps,transport,creatures,,,,
刷新所有running battlegrounds
刷新SqlResultQueue, 逻辑层和数据层是通过Queue来进行异步操作的(用了AsyncPQuery和SqlResultQueue)
处理尸体移除
处理游戏事件
处理 Move all creatures with "delayed move" and remove and delete all objects with "delayed remove"
处理InstanceSaveManager的刷新
调用World::ProcessCliCommands,处理CLI从cliCmdQueue取得cmd进行解析执行所有有效的Cmd,都可以在ChatHandler::getCommandTable中找到
类WorldSession: 类WorldSession负责逻辑处理
void WorldSession::SendPacket(WorldPacket const* packet) 负责发包给客户端,直接发包,没有输出缓冲队列
在WorldSession::Update中进行逻辑处理World::UpdateSessions会调用所有WorldSession的WorldSession::Update
执行语句OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];得到opHandle
根据得到的opHandle,执行(this->*opHandle.handler)(*packet);
WorldSession::HandlePlayerLogin处理玩家登陆游戏
构建Player
Player::LoadFromDB从数据库中加载玩家数据在Player::LoadFromDB中会调用SetMap(MapManager::Instance().CreateMap(GetMapId() , this));加载当前player所在的map
Player::SetPosition在Player运动的时候,改变位置,保存处理夸区
类Map实现了一个state machine,采用state pattern组织了Gid的4个state object:InvalidState;ActiveState;IdleState;RemovalState
game中的管理器有: ObjectMgr
mMangosStringLocaleMap 关联到mangos_string table
m_scriptNames 关联到tables: creature_template;gameobject_template;item_templat e;areatrigger_scripts;instance_template
mCreatureLocaleMap 关联到locales_creature table
mGameObjectLocaleMap关联到locales_gameobject table
mItemLocaleMap关联到locales_item table
mQuestLocaleMap –> locales_quest
mNpcTextLocaleMap –> locales_npc_text
mPageTextLocaleMap –> locales_page_text
mGossipMenuItemsLocaleMap –> locales_gossip_menu_option
mPointOfInterestLocaleMap –> locales_points_of_interest
…
对象类层次
对象的类层次如下,所有的Object都由ObjectMgr进行管理ObjectMgr以GUID方式,管理了characters,creature,item_instance,gameobject,auctionhouse,mail,item_text,corpse,arena_team,character_equipmentsets
Player状态
/// Player state
enum SessionStatus
{
STATUS_AUTHED = 0, ///< Player authenticated (_player==NULL, m_playerRecentlyLogout = false or will be reset before handler call, m_GUID have garbage)
STATUS_LOGGEDIN, ///< Player in game (_player!=NULL, m_GUID == _player->GetGUID(), inWorld())
STATUS_TRANSFER, ///< Player transferring to another map (_player!=NULL, m_GUID == _player->GetGUID(), !inWorld())
STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT, ///< _player!= NULL or _player==NULL && m_playerRecentlyLogout, m_GUID store last _player guid)
STATUS_NEVER ///< Opcode not accepted from client (deprecated or server side on
};
生物状态
enum DeathState
{
ALIVE = 0,
JUST_DIED = 1,
CORPSE = 2,
DEAD = 3,
JUST_ALIVED = 4,
DEAD_FALLING= 5
};
玩家登陆
服务器端在连接打开后,会发SMSG_AUTH_CHALLENGE到客户端客户端从服务器端发送回来的种子和 SRP6 数据中产生随机种子,生成 SHA1 字符串,用这些数据生成 CMSG_AUITH_SESSION 数据包,发送给服务端这个过程是没有经过加密的
客户端发送SMSG_AUTH_SESSION到服务器
服务器处理SMSG_AUTH_SESSION
服务器发送SMSG_AUTH_RESPONSE给客户端
服务器发送SMSG_ADDON_INFO给客户端
服务器发送SMSG_CLIENTCACHE_VERSION给客户端
服务器发送SMSG_TUTORIAL_FLAGS给客户端
packet结构 SMSG_AUTH_SESSION 是client packet有一个头部(ClientPktHeader),后面是数据块
SMSG_AUTH_RESPONSE 是server packet有一个头部(ServicePktHeader),后面是数据块
SMSG_AUTH_RESPONSE 的包组织
SMSG_AUTH_RESPONSE 的opcode是01EE,ByteBuffer大小为1 + 4 + 1 + 4 + 1=11一个SMSG_AUTH_RESPONSE 的数据如下: 在构造了SMSG_AUTH_RESPONSE packet后,WorldSocket::SendPacket会根据SMSG_AUTH_RESPONSE packet构造出一个ServerPktHeader,并对ServerPktHeader中的数据header进行加密发送加密采用m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength()); WorldSocket::handle_input_header会对从客户端接收来的数据进行解密,解密采用m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr (), sizeof (ClientPktHeader));
角色枚举 玩家登上服务器后,从客户端发送SMSG_CHAR_ENUM到服务器
在服务器端
根据{ "CMSG_CHAR_ENUM", STATUS_AUTHED, &WorldSession::HandleCharEnumOpcode },服务器会调用WorldSession::HandleCharEnumOpcode
WorldSession::HandleCharEnumOpcode会调用CharacterDatabase.AsyncPQuery,进行异步查询后,调用CharacterHandler::HandleCharEnumCallback
CharacterHandler::HandleCharEnumCallback会回头调用session->HandleCharEnum(result);
WorldSession::HandleCharEnum根据characters数据库的查询结果,调用Player::BuildEnumData,加载角色数据,构造SMSG_CHAR_ENUM返回包,然后发送回客户端
角色创建
角色的初始化装备在CharStartOutfit.dbc
角色的创建和选择的设置都在 ChrRaces.dbc
角色的创建属性都在Playercreateinfo,包括出生地和出生属性
playercreateinfo_item表是创建一个新人物时,人物默认带的所有Item的表
playercreateinfo_spell表是创建一个新人物时,人物默认带的所有Spell的表
角色删除
最后
以上就是文艺雪糕为你收集整理的mangos代码阅读的全部内容,希望文章能够帮你解决mangos代码阅读所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复