local skynet = require "skynet" local oo = require "Class" local gameCmd = require "GameCmd" local json =require "json" local log = require "Log" local sqlUrl = require "SqlUrl" local defense = require "Defense" local playerFields = require "PlayerFields" local errorInfo = require "ErrorInfo" local serverId = tonumber(skynet.getenv "serverId") local clusterServer = require "ClusterServer" local redisKeyUrl = require "RedisKeyUrl" local RouteServer = oo.class(clusterServer) --初始化 function RouteServer:Init() --self.serverInfo = {} --服务器当前信息 self.platformType = {} self.platformType["Apple"] = true self.platformType["Android"] = true self.serverInfo.isStopServer = false --是否停止服务器 self.serverInfo.isCanUserEnter = true --用户是否能进入 self.serverInfo.maxPlayerCountPerGS = 20000 --每个游戏服最大人数 self.serverInfo.tokenBucketCurCount = 200 --令牌漏桶当前数量 self.serverInfo.tokenBucketAddCount = 30 --令牌漏桶每5秒新增数量 self.serverInfo.tokenBucketMaxCount = 300 --令牌漏桶最大数量 self.lastQueryGameServerInfoTime = skynet.GetTime() --上一次查询大厅服时间 end --跨天 function RouteServer:OnNewDay() if not self:IsRouteServer( serverId ) then return end end --1秒Timer function RouteServer:On1SecTimer() if not self:IsRouteServer( serverId ) then return end end --5秒Timer function RouteServer:On5SecTimer() if not self:IsRouteServer( serverId ) then return end --每5秒新增令牌桶的数量 self.serverInfo.tokenBucketCurCount = self.serverInfo.tokenBucketCurCount + self.serverInfo.tokenBucketAddCount if self.serverInfo.tokenBucketCurCount > self.serverInfo.tokenBucketMaxCount then self.serverInfo.tokenBucketCurCount = self.serverInfo.tokenBucketMaxCount end end --接收集群数据 function RouteServer:ClusterRecv(...) local cmd , c2sData = ... local s2sData = {} s2sData.code = errorInfo.Suc if self.Center2All_ServerManageCmd == cmd then elseif self.Center2All_SyncClusterInfo == cmd then self:RecvSyncClusterInfo( c2sData ) elseif self.Center2All_SyncServerConfig == cmd then --同步服务器配置 self:RecvSyncServerConfig( c2sData ) else log.info(string.format("集群服务器 消息接口 %d 不存在", cmd)) s2sData.code = errorInfo.ErrorCode.NoExistInterface end log.info(string.format("集群服 执行命令 %d 返回消息状态 %d " , cmd , s2sData.code)) return s2sData end --接收HTTP数据 function RouteServer:HttpRecv( c2sData , url , addr ) local s2cData = {} s2cData.code = errorInfo.Suc c2sData.addr = addr --禁止进入 if not self.serverInfo.isCanUserEnter then s2cData.code = errorInfo.ErrorCode.ForbidUserLogin return s2cData end --令牌桶数量不足 if 0 == self.serverInfo.tokenBucketCurCount then s2cData.code = errorInfo.ErrorCode.TokenCountNotEnough return s2cData end if "GetGateUrl" == url then self:GetGateUrl( c2sData , s2cData ) elseif "SetConfig" == url then self:SetConfig( c2sData , s2cData ) elseif "health" == url then self:Health( c2sData , s2cData ) else log.info(string.format("Http服务器 消息接口 %s 不存在", url)) s2cData.code = errorInfo.ErrorCode.NoExistInterface end log.info("Http服务器 消息接口 ",url ,"返回信息",s2cData.code) return s2cData end --获取网关地址 function RouteServer:GetGateUrl( c2sData , s2cData ) --验证登陆接口参数 if nil == c2sData or nil == c2sData.platform or nil == c2sData.loginAccount then s2cData.code = errorInfo.ErrorCode.ErrRequestParam return s2cData end --检查平台合法性 --[[ if not self.platformType[c2sData.platform] then s2cData.code = errorInfo.ErrorCode.NoExistPlatformType return s2cData end ]] --是否停止维护 if self.serverInfo.isStopServer then s2cData.code = errorInfo.ErrorCode.StopServer return s2cData end log.info(string.format("平台 %s 玩家 %s 开始查找玩家", c2sData.platform , c2sData.loginAccount )) self:IsVerifyValid( c2sData , s2cData ) --令牌数量减1 self.serverInfo.tokenBucketCurCount = self.serverInfo.tokenBucketCurCount - 1 local gameServerId = nil --向每个在线的网关进行询问用户是否在线,不在线就找人数最少的网关 local clusterReturn = {} local isExistPlayer = nil for k, v in pairs(self.clusterInfo) do --不管游戏服是运行还是停止状态,只要该玩家在里面,就能让该玩家继续登陆这台游戏服 if self:IsGameServer( v.serverId ) and v.status == self.Status_Running or v.status == self.Status_Stop then clusterReturn = self:CallMsgToServer(v.serverId , self.Route2Game_QueryUserOnline , c2sData ) if errorInfo.Suc == clusterReturn.code and clusterReturn.isExistPlayer then gameServerId = v.serverId log.info(string.format("平台 %s 玩家 %s 已经在游戏服 %d", c2sData.platform , c2sData.loginAccount , gameServerId )) break end --[[ --从redis中检查各个游戏服中有不有该玩家 local redisKey = string.format(redisKeyUrl.AccountServerOnlinePlayerList , c2sData.platform , v.serverId ) isExistPlayer = skynet.server.redis:sismember( redisKey , c2sData.loginAccount ) if isExistPlayer then gameServerId = v.serverId log.info(string.format("平台 %s 玩家 %s 已经在游戏服 %d", c2sData.platform , c2sData.loginAccount , v.serverId )) break end ]] end end --优先给需要玩家的游戏服,好做压力测试 if not gameServerId then for k, v in pairs(self.clusterInfo) do if self:IsGameServer( k ) and v.status == self.Status_Running then local curCount = v.serverInfo.playerCount.offline + v.serverInfo.playerCount.playing if 0 ~= v.serverInfo.needPlayerCount and curCount < v.serverInfo.needPlayerCount then gameServerId = v.serverId break end end end end --寻找人数最少的网关 if not gameServerId then local minOnlineCount = -1 local curCount = 0 for k, v in pairs(self.clusterInfo) do --分配玩家到运行状态的游戏服 if self:IsGameServer( k ) and v.status == self.Status_Running then curCount = v.serverInfo.playerCount.offline + v.serverInfo.playerCount.playing if ( -1 == minOnlineCount or curCount < minOnlineCount ) and curCount <= self.serverInfo.maxPlayerCountPerGS then gameServerId = v.serverId minOnlineCount = curCount end end end end if not gameServerId or not self.clusterInfo[ gameServerId ] then log.info("未找到该游戏服信息" , gameServerId) s2cData.code = errorInfo.ErrorCode.GetGateUrlFailed return end log.info(string.format("玩家 %s 获得游戏服登陆许可 %d",c2sData.loginAccount , gameServerId)) --同步Token到指定的大厅服 local loginToken = skynet.server.common:RandToken() local cfgServer = nil for k, v in pairs( skynet.server.gameConfig.ClusterServerConfig ) do if gameServerId == v.serverId then cfgServer = v break end end if not cfgServer then log.info("未加找相关服的配置" , gameServerId) s2cData.code = errorInfo.ErrorCode.GetGateUrlFailed return end --向指定的游戏服发送玩家token s2cData.loginIP = cfgServer.externalIp s2cData.loginPort = cfgServer.tcpPort s2cData.loginToken = loginToken self:SendTokenToGameServer( gameServerId , c2sData.platform ,c2sData.loginAccount , loginToken ) return end --设置配置 function RouteServer:SetConfig( c2sData , s2cData ) if nil == c2sData.key or nil == c2sData.value then s2cData.code = errorInfo.ErrorCode.ErrRequestParam log.info(string.format("错误的请求参数 key %s value %s" , c2sData.key , c2sData.value)) return end if nil == self.serverInfo[ c2sData.key ] then s2cData.code = errorInfo.ErrorCode.ErrRequestParam log.info(string.format("不存在的请求参数 key %s value %s" , c2sData.key , c2sData.value)) return else log.info(string.format("修改前参数 key %s value %s" , c2sData.key , self.serverInfo[ c2sData.key ] )) self.serverInfo[ c2sData.key ] = c2sData.value log.info(string.format("修改后参数 key %s value %s" , c2sData.key , self.serverInfo[ c2sData.key ] )) end end --健康管理 function RouteServer:Health( c2sData , s2cData ) s2cData.code = 200 end --发送登陆TOKEN到登陆服务器 function RouteServer:SendTokenToGameServer( serverId , platform , loginAccount , loginToken) local clusterMsg = {} clusterMsg.platform = platform clusterMsg.loginAccount = loginAccount clusterMsg.loginToken = loginToken self:SendMsgToServer(serverId , self.Route2Game_UserLoginToken , clusterMsg) end --验证是否有效 function RouteServer:IsVerifyValid( c2sData , s2cData ) for k, v in pairs(self.clusterInfo) do if self.monitorServerID == v.serverId and self.Status_Running == v.status then --大厅服没有开,就不用网络服验证,直接通过 s2cData = self:CallMsgToServer( v.serverId , self.Route2Monitor_VerifyValid , c2sData) if not s2cData then s2cData.code = errorInfo.ErrorCode.NoExistServer log.info(string.format("集群服serverId %d 不在线", serverId)) return false else local isBlack = false if errorInfo.ErrorCode.BlackListAccount == s2cData.code then log.info(string.format("路由服 平台 %s 帐号 %s 在帐号黑名单中" , c2sData.platform , c2sData.loginAccount)) isBlack = true elseif errorInfo.ErrorCode.BlackListAddr == s2cData.code then log.info(string.format("路由服 平台 %s 帐号 %s 在IP黑名单中" , c2sData.platform , c2sData.loginAccount)) isBlack = true end if isBlack then return false else return true end end end end return true end --如果是当前服务器ID,那么就加入进来 if serverId >= clusterServer.routerServerMinID and serverId <= clusterServer.routerServerMaxID then skynet.server.routeServer = RouteServer end return RouteServer