服务器比较
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 }