291 lines
11 KiB
Lua
291 lines
11 KiB
Lua
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 |