1.两个框架
mobile legends
{
微服务架构:各系统耦合性低,rpc
AccountServer DirServer
LoginServer---c(账号认证,角色信息,区服信息)
|
|
ConnServer ConnServer ConnServer---c
GameServer GameServer GameServer---SharedMemory---MySql
RankServer FriendServer
TeamServer MatchServer --- BattleServer BattleServer
PingServer BroadServer --- BattleServer BattleServer
epoll线程(Server)(1-N) ServiceHandleThread(1-N)
epoll线程(Client)(1-N) AsyncRspThread(1-N)
附上一个配置,更好理解
<mfw>
<application>
setdivision = moba.zone.2006
<client>
locator = mfw.mfwregistry.QueryObj@tcp -h 192.168.40.220 -p 2000//注册服务器地址
refresh-endpoint-interval = 60000
sync-invoke-timeout = 3000
async-invoke-timeout = 5000
connect-timeout = 1000
asyncthread = 3
</client>
<server>
app = MOBA
server = GameServer
logpath = ~/log
loglevel = DEBUG
framework-loglevel = INFO
log = mfw.mfwlog.LogObj
loop-interval = 50
admin = tcp -h 127.0.0.1 -p 0 -t 10000
agent-report = mfw.mfwagent.AgentReportObj@tcp -h 127.0.0.1 -p 2002 -t 3600000
<Service_1>
service = MOBA.GameServer.GameServiceObj
endpoint = tcp -h 192.168.40.210 -p 2024 -t 600000
protocol = mfw
threads = 1
maxconns = 1024
queuecap = 10240
queuetimeout = 5000
</Service_1>
</server>
</application>
</mfw>
}
300英雄
{
整体性架构:耦合性高
ShareMemProc:只申请共享内存
LocalSaveServer读取共享内存发送到--->ManagerServer
Client
|
|
GateServer(多个)--->AuServer(账号验证)
|
|
|
GameDB---ManagerServer--->(GameAIServer,NameServer,CenterServer---GameDB)
|
|
|
GameServer(多个)---ShareMemProc----LocalSaveServer
IOCP与epoll最大不同:IOCP时间通知时,已经做好了I/O,而epoll,还得自己去读写。
CIOCPSessionIdleRunThread::StartIdleRunThread CIOCPSessionIdleRunThread::IOCPIdleRunThread CManagerServerConnector 1个
CIOCPSessionWorkThread::StartWorkThread CIOCPSessionWorkThread::IOCPWorkThread CManagerServerConnector 10个
GateListener CTcpServer::RunByNewThread 1个
CClientBattleListener CTcpServer::RunByNewThread 1个
CThreadServer::LogicProcessThreadFunc GameServer逻辑线程
CThreadServer::TimeoutDumpThreadFunc TimeOutDump线程
Log线程若干个
main线程
}
2.两个登录流程
mobile legends
{
DirServer
增加,修改-后台网页操作
维护所有分区的当前信息
创建,修改,上报分区信息
分区:状态,版本号,注册量等
一.loginserver
定时从dir同步所有区服的信息
1.CmdId_Login_Auth_CS->LoginServer
1_1:ip获得国家,然后根据渠道看是否禁止登陆
1_2:到accountserver获取账号信息,创建账号信息
1_3:推荐分区(最近登录的一个分区)
2.CmdId_Login_GetZone_CS->LoginServer获取一个账号的相关角色信息(内网没有调用)
所有区服和角色的基本信息
3.Cmd_Login_CheckUpgrade_CS
各种地址,包括connectserver的地址,cdn头像上传等
4.CmdId_Login_GetBulletin_CS
获得游戏公告
5.Cmd_Login_UpdateDeviceToken_CS(内网没有调用,可能在某些情况下调用)
上报devicetoken
6.Cmd_Login_GetRegion_CS(内网没有调用)
各个大区的信息
二.与connserver连接
CmdId_Net_Connect_CS:连接connserer
CmdId_Net_LogReport_CS:上报客户端日志
CmdId_Role_Init_CS:获取角色详细信息
CmdId_Net_Idle_CS:维护客户端连接心跳
CmdId_Account_GetAccount_CS:获取账号相关信息
三.与GameServer连接
CmdId_Net_Connect_CS
CmdId_Net_SetSkeyExpireTime_CS:ConnServer 通知 GameServer 过期时间
CmdId_Role_Init_CS
CmdId_Account_GetAccount_CS
CmdId_Net_ServerTime_CS
}
300英雄
{
帐号验证
gate -> client MsgLoginDataWrapper(Sg2cSecretKey)
client -> gate MsgLoginDataWrapper(Sc2gReqClientLogin) //登陆消息
gate -> audbgate Sg2aReqClientLogin //验证服务器(通过登陆账号密码返回具体玩家账号信息)
audbgate -> gate(Sa2gAckClientLogin) -> client(Sg2cAckClientLogin) //返回具体玩家账号信息
gate -> manager MsgReqLoginCheck
manager -> gate MsgAckLoginCheck
gate -> manager DBMsgReqGetAllRoles //获取所有角色信息
manager -> db Sm2dReqAllRoles
db -> manager Sd2mAckAllRoles
manager -> gate MsgResGetAllRoles
gate -> client MsgResGetAllRoles 1287 如果m_btRoleNum数量为0则进入创建流程,如果大于0则进入选角色登入流程
登入流程
client -> gate MsgEnterWorld //获取角色信息
gate -> manager DBMsgReqGetOneRole
manager -> db Sm2dReqGetOneRole
db -> manager Sd2mAckGetOneRole
1、Client-连接->GateServer GateServer保存会话OnNewClientConnected
2、Client-发送MSG_LOGINDATAWRAPPER->GateServer 登陆消息
3、GateServer-登陆消息->AuAgent 验证服务器(通过登陆账号密码返回具体玩家账号信息)
4、AuAgent-返回具体玩家账号信息->GateServer GetAU().Run()-ProcessAUAgentProtocol-通知客户端验证-OnAckLogin-更新1会话
5、ManagerServer-MSG_S2C_RESPONSEGETALLROLES->GateServer ManagerServer发送人物信息给玩家,早期可能是GameServer发送的
6、Client-MSG_ENTERWORLD->GateServer-MSG_GMD_REQUESTGETONEROLE->ManagerServer 获取角色信息
7、ManagerServer-em2dReqGetOneRole->DB(CAccountManager) 向账号管理请求角色信息(DBServerConnector)
8、DB->ed2mAckGetOneRole->ManagerServer DB返回数据至ManagerServer
9、ManagerServer->MSG_DMG_RESPONSEGETONEROLE->GameServer ManagerServer将DB数据组装发给GameServer,GameServer更新玩家信息
10、GameServer-MSG_ACKENTERWORLD->GateServer->Client 进入世界 清除临时登陆信息m_TempSessions、m_LoginSessions 建立以dwPlayerCharacterID为索引的会话m_GameChars
}
3.两个战斗流程
mobile legends
{
1.创建房间
CmdId_Room_Create_CS
1_1到pingserver获取ping返回(格式:groupid:ping值)
遍历这些battlegroup,检测battlegroup的版本(t_battle_group),在所有battle中找(t_battle_status)这个版本号和状态时正常的,确定本次匹配的matchid
game上有一个battlegroup->matchid的列表,是从数据库中取得
取第一个battlegroup的matchid,然后过滤后面的battlegroup的matchid。获得一个battlegroup的列表。
CmdId_Room_Enter_CS(单人匹配不会调用)
进入房间,根据模式,判断各种限制条件(段位,版本号等)
CmdId_Battle_StartMatch_CS
从roomserver获取房间里面的玩家信息,开始匹配,根据玩家身上存储的matchid选择在哪个matchid进行匹配,会将房间里面的所有玩家信息带到match上
发送CmdId_Match_StartMatch MatchServiceMsg到match上,battlegroup信息也带到match上
根据带到match上的battlegroupu列表,筛选出可用的battlegroup列表(版本号,在线人数),然后同时在这些battlegroup里面匹配,扔到几人匹配的list里面
loop每一帧从list匹配玩家,匹配成功后找到这个battlegroup里面的battle,加入到这个battle上面比赛,在一个battlegroup里面匹配成功了,则删除其他battlegroup里面匹配的该玩家
1.分battlegroup,到battlegroup中匹配
2.每个battlegroup分模式,相同模式一起匹配
3.分人数(1,2,3,4,5) 不同人数在不同的list中,计算单人或者组队的匹配值,list按照匹配值排序从小到大
4. ProcessList5(); 判断两两之间能不能匹配,遍历列表迭代器和前一个迭代器的队伍能不能匹配
ProcessList4(); (4,1)->5
ProcessList3(); (3,2)->5 (3,1)->4
ProcessList2(); (2,1)->3
ProcessList1(); 遍历单人列表,迭代器向前向后找到5个人队列中
ProcessList0(); 已匹配好的删除掉
5.找battle 遍历所有battle,然后找这个匹配的battlegroupid(m_uiSvrID/1000),找出人最少的,到相应的battle上创建战斗,battle会上报数据到match上
6.battle上报信息到match上,gameserver上面会从battlegroupid->match的列表
再BattleServer上创建战斗
HandleConn::dispatchOneCommandByMain
使用了一个状态模式
CmdId_Battle_Oper_CS
Battle::DispatchBattleOper,玩家操作m_vecBattleOperData.push_back(stBattleOper);
CmdId_Battle_Result_CS
客户端上报战报
CmdId_Battle_Hash_CS
客户端上报MD5
pPlayer->m_strMD5 = stReq.sMd5
66ms一帧
1.Battle::LogicUpdate,将66ms内玩家的操作广播给10个玩家
2.检测MD5,取大多数为正确的MD5,则其他人不正确
3.比对战报的MD5,拿大多数人的结果作为服务器战报
}
300英雄
{
ManagerServer->GameAIServer
CELO_Super_Queue-CElo_Base_Queue-m_listNode:不同类型的战斗排队列表
匹配成功
GameAIServer->ManagerServer
ManagerServer(m_quAthReady保存已就位的队伍信息)->GameServer
CAthleticsManager::SendReadyTeamToGS:找人数最少的game
GameServer开始创建副本,切线(比如从1线切到3线)
1.玩家从1线下线,存储玩家的信息,并在ManagerServer上设置玩家处于切线状态
2.玩家重新登录DBMsgReqGetOneRole,然后登录到玩家要切的那条线上EnterWorld
3.客户端直连GameServer开始战斗
4.进入到玩家的副本中,里面有个状态机控制玩家当前进入到了哪个阶段
5.结束之后玩家再次走切线流程,切换到原来的线中
AI:ai玩家注册一个100ms的定时器,给ai分配了总共18个任务,
1.根据任务的不同执行不同的ai脚本
2.ai有不同的状态,根据状态的不同调用不同状态的update
技能:
1.考虑施法距离,如果距离不够,移动到目标点
2.技能开始释放分阶段
前摇
攻击(吟唱,引导),结算:结算自己,结算目标(前,时,后)
后摇
这里面很多都是通过状态实现,大部分是策划脚本加的状态,玩家身上有个状态列表,然后定时器驱动这些状态执行动作
怪物AI:
空闲(IDEL)
|
|(受到攻击,从附近找一个最近的可攻击,保持攻击2s,加入到攻击列表)
|
战斗状态
目标死亡:丢失目标LostTarget(从攻击列表中找最近)
|
|
(能否保持攻击)
否:丢失目标 LostTarget(从攻击列表中找最近)
是 { 1. 2s的保持攻击结束,从最近攻击的列表中寻找最近的继续攻击
2.继续攻击之前的目标
}
状态机实现
视野:
1.视野组
2.附近的玩家
3.九宫格通知
定时器实现
1.高等级定时器
数量少,时间短,直接是一个vector,遍历执行,这些定时器和其他的分开单独执行
2.
100ms 0~20 20~40 20~40 ... 180~200
100ms_1 200~300 300~400 ... 1100~1200
long 1200~2200 10200~11200
>11200
新的根据定时器的执行时间插入到对应的队列中
老的也会遍历每个slot中的,计算时间放到上一层对应的slot中,最终到时间了放入m_waitExecute待执行列表中
接下来直接执行m_waitExecute中的定时器。
}
4.玩家数据管理
mobile legends
{
共享内存mmap
CMemKV m_mBlob; ---管理共享内存
玩家上线从m_mBlob反序列化出该玩家信息 到unordered_map中
1.初始化共享内存
2.从db中获取所有玩家数据,放入共享内存中
3.owner.setChanged():同步到共享内存中
4.BlobManager::loop 从m_mBlob中取出脏的blob放入异步队列中,这步有个令牌桶限流
5.存储DB线程从异步队列中取出更新到数据库
GetRoleByUid:首先从内存中获取,获取不到从m_mBlob反序列化出来
实在看不懂MEMKV也没有任何资料介绍
}
300英雄
{
hash_map:玩家id->到共享内存块的映射
玩家上线:
1.如果已经在共享内存,直接拿过来用
2.从共享内存中找到一块空闲的给该玩家,
3.注册一个定时器5分钟,同步一次数据到ShareMemory,也可以自己手动同步
LocalSaveServer 将共享内存里面的脏数据数据发送到Manager再到DBServer
}