using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Sog; using ProtoCSStruct; namespace World { public static class GameReportSvc { //上报时间间隔,服务器启动的时候会变 private static int m_ReportAccountSecondInterVal = 5; //最大上报时间间隔, 也不能太慢,realmList进程需要及时知道个game进程的online情况,用来做选择 private const int ReportAccountSecondInterValMax = 20; private static long lastReportAccountTime = 0; private static long lastLogOnlineTime = 0; private static long lastForceGetAllRealmTime = 0; public static int WorldOnlinePlayerCount { get; private set;} //game或者gamegate的上报 public static void OnGameReportReq(uint remoteAppID, StructPacket packet) { ref SSGameReportReq gameReportReq = ref packet.GetMessage(); int onlinePlayer = gameReportReq.OnlinePlayer; bool find = false; var svrData = WorldServerUtils.GetWorldServerData(); foreach (var info in svrData.m_gameSvrInfo) { if (info.serverId == gameReportReq.GameSvrid) { info.onlinePlayer = onlinePlayer; find = true; break; } } if (find == false) { svrData.m_gameSvrInfo.Add(new GameSvrInfoWorld {serverId = gameReportReq.GameSvrid, onlinePlayer = onlinePlayer}); } //如果是game,要判断md5是否已经成功拉取 if (ServerIDUtils.GetServerType(gameReportReq.GameSvrid) == (int)ServerType.Game) { //如果有game进程没有获取成功RecvConfigRealmListMd5, 则强制全量拉取 if (gameReportReq.RecvConfigRealmListMd5.IsEmpty()) { svrData.recvRealmListMd5 = null; m_ReportAccountSecondInterVal = 2; //马上发起一次查询 TraceLog.Trace("SysSvc.OnGameReportReq from server {0}, RecvConfigRealmListMd5 is empty , force clear svrData.recvRealmListMd5" , ServerIDUtils.IDToString(gameReportReq.GameSvrid)); } } TraceLog.Trace("SysSvc.OnGameReportReq from server {0}, online {1}" , ServerIDUtils.IDToString(gameReportReq.GameSvrid) , gameReportReq.OnlinePlayer); } public static void OnRealmOnlineReport(uint remoteAppID, StructPacket packet) { ref SSOnlineReport report = ref packet.GetMessage(); var onlineTable = WorldServerUtils.GetWorldServerData().realmOnlineGame; // 一个realm可以分布在多个gamesvr上, 一个gamesvr可以有多个realm // 统计每个gamesvr上的realm在线信息, 然后在tick时累加获得realm的实际在线 List gameSvrOnline; if (! onlineTable.TryGetValue(remoteAppID, out gameSvrOnline)) { gameSvrOnline = new List(); onlineTable.Add(remoteAppID, gameSvrOnline); } for (int i = 0; i < report.RealmOnline.Count; i++) { int realmId = report.RealmOnline[i].Id; int onlineNum = report.RealmOnline[i].Value; RealmOnline info = gameSvrOnline.FirstOrDefault(d => d.realmId == realmId); if (info != null) { info.onlineNum = onlineNum; } else { info = new RealmOnline {realmId = realmId, onlineNum = onlineNum}; gameSvrOnline.Add(info); } } } public static void OnGameReportRealmlistRes(uint remoteAppID, StructPacket packet) { //备机不处理 if(AccountServerSelect.IsBackAccountServer(remoteAppID)) { return; } ref SSGameReportRealmlistRes res = ref packet.GetMessage(); var serverData = WorldServerUtils.GetWorldServerData(); //需要这个临时保存列表,是为了防止2个消息过程中有不完整的configRealm的情况发生 var tmpRealmsRecv = serverData.m_configRealmTempForRecv; //0 表示realmList服务器还没有更新,是老的, 1表示是新的realmList发出的第一个消息 if (res.MsgIndexFrom1 == 0 || res.MsgIndexFrom1 == 1) { tmpRealmsRecv.Clear(); } //先加入临时列表 for (int i = 0; i < res.RealmList.Count; i++) { var realm = new RealmBriefInfo(ref res.RealmList[i]); tmpRealmsRecv[realm.realmId] = realm; } //如果是最后一个消息了 if (res.MsgIndexFrom1 == 0 || res.RealmList.Count == 0) { serverData.m_configRealm.Clear(); serverData.m_configRealmMap.Clear(); var recordInfo = WorldServerUtils.GetWorldMainlandRecordData(); //排个序 var list = tmpRealmsRecv.OrderBy(o => o.Key).ToList(); for (int i = 0; i < list.Count; i++) { var realm = list[i].Value; serverData.m_configRealm.Add(realm); serverData.m_configRealmMap.Add(realm.realmId, realm); if (!recordInfo.m_logicWorldMap.ContainsKey(realm.logicWorldId)) { } } serverData.recvRealmListMd5 = res.RealmListMd5.GetString(); } TraceLog.Trace("GameReportSvc.OnGameReportRealmlistRes m_configRealm count {0}, m_configRealmTempForRecv count {1}", serverData.m_configRealm.Count, serverData.m_configRealmTempForRecv.Count); //广播给所有game WorldServerUtils.GetPacketSender().Broadcast((int)ServerType.Game, packet); } public static void OnTick(long nowSec) { TickNotifyAccountGateCount(nowSec); TickLogOnlineReportWorldOnline(nowSec); } public static void TickNotifyAccountGateCount(long nowSec) { if (nowSec - lastReportAccountTime < m_ReportAccountSecondInterVal) { return; } lastReportAccountTime = nowSec; if (m_ReportAccountSecondInterVal < ReportAccountSecondInterValMax) { m_ReportAccountSecondInterVal += 1; } var reportReq = new SSGameReportRealmlistReq(); var svrData = WorldServerUtils.GetWorldServerData(); int iAllServerOnlinePlayer = 0; foreach (var info in svrData.m_gameSvrInfo) { if (ServerIDUtils.GetServerType(info.serverId) == (int)ServerType.Game) { iAllServerOnlinePlayer += info.onlinePlayer; } if(ServerIDUtils.GetServerType(info.serverId) == (int)ServerType.GameGate || ServerIDUtils.GetServerType(info.serverId) == (int)ServerType.ChatGate) { GameGateSvrInfo gateInfo; gateInfo.GateSvrid = info.serverId; gateInfo.OnlinePlayer = info.onlinePlayer; reportReq.Gates.Add(ref gateInfo); } } //上报world的在线(包括gate的连接数量)给account->realmlist, reportReq.OnlinePlayer = iAllServerOnlinePlayer; reportReq.WorldSvrid = WorldServerUtils.GetAppID(); //为了以防万一,每10分钟强制拉一次全量 if (nowSec - lastForceGetAllRealmTime >= 600) { lastForceGetAllRealmTime = nowSec; } else { reportReq.RealmListMd5.SetString(svrData.recvRealmListMd5); } // 广播给主机和备机上的RealmsList WorldServerUtils.GetPacketSender().Broadcast((int)ServerType.RealmsList, (int)SSMsgID.GameReportRealmlistReq, ref reportReq, 0 , 0); WorldOnlinePlayerCount = iAllServerOnlinePlayer; } public static void TickLogOnlineReportWorldOnline(long nowSec) { //1分钟1次就行了,其实也没啥用 if (nowSec - lastLogOnlineTime < 60) { return; } lastLogOnlineTime = nowSec; var svrData = WorldServerUtils.GetWorldServerData(); int iAllServerOnlinePlayer = 0; foreach (var info in svrData.m_gameSvrInfo) { if (ServerIDUtils.GetServerType(info.serverId) == (int)ServerType.Game) { iAllServerOnlinePlayer += info.onlinePlayer; } } //上报全服人数 // 写 bill 日记 WorldBillLogUtils.LogOnlinePlayer(iAllServerOnlinePlayer); } // 统计每个realm的在线数据 public static void TickRealmOnline() { var allOnline = WorldServerUtils.GetWorldServerData().realmOnlineAll; allOnline.Clear(); var gameOnline = WorldServerUtils.GetWorldServerData().realmOnlineGame; // 把不同gamesvr上的同一个realm的在线人数累加起来 foreach (List realmList in gameOnline.Values) { foreach (RealmOnline realmOne in realmList) { if (! allOnline.ContainsKey(realmOne.realmId)) { allOnline[realmOne.realmId] = realmOne.onlineNum; } else { allOnline[realmOne.realmId] += realmOne.onlineNum; } } } SSRealmOnlinePlayerToOpsvr req = new SSRealmOnlinePlayerToOpsvr(); foreach (KeyValuePair kvp in allOnline) { RealmOnlineInfo info = new RealmOnlineInfo(); info.RealmID = kvp.Key; info.OnlinePlayer = kvp.Value; if (req.RealmList.Count < req.RealmList.GetMaxCount()) { req.RealmList.Add(info); } else { // 同步发送到 op WorldServerUtils.GetPacketSender().Broadcast((int)ServerType.Operation, (int)SSMsgID.RealmOnlineplayerToOpsvr, ref req, 0, 0); req.RealmList.Clear(); req.RealmList.Add(info); } } if (req.RealmList.Count > 0) { // 同步发送到 op WorldServerUtils.GetPacketSender().Broadcast((int)ServerType.Operation, (int)SSMsgID.RealmOnlineplayerToOpsvr, ref req, 0, 0); } } } }