417 lines
15 KiB
Lua
417 lines
15 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 redisKeyUrl = require "RedisKeyUrl"
|
|||
|
|
local pb = require "pb"
|
|||
|
|
local dbData = require "DBData"
|
|||
|
|
local player = require "Player"
|
|||
|
|
local dataType = require "DataType"
|
|||
|
|
|
|||
|
|
local AccountServer = oo.class()
|
|||
|
|
AccountServer.Platform_local = "Local"
|
|||
|
|
AccountServer.Platform_WeChat = "WeChat"
|
|||
|
|
|
|||
|
|
AccountServer.PerSaveMaxCount = 10 --每次保存最多个数
|
|||
|
|
|
|||
|
|
--初始化
|
|||
|
|
function AccountServer:Init()
|
|||
|
|
|
|||
|
|
self.platformType = {}
|
|||
|
|
table.insert( self.platformType , "Apple")
|
|||
|
|
|
|||
|
|
--初始化默认DB索引(千成不能删除,非常重要 开始)!!!!!!!!!!!!!!!
|
|||
|
|
--[[
|
|||
|
|
local key = string.format(redisKeyUrl.AccountServerCurDBIndex )
|
|||
|
|
if not skynet.server.redis:exists( key) then
|
|||
|
|
skynet.server.redis:set( key , 0 )
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
for i = 1, skynet.server.gameConfig.DBInfoConfig.playerTableCount, 1 do
|
|||
|
|
key = string.format(redisKeyUrl.AccountServerUserDBID , i )
|
|||
|
|
if not skynet.server.redis:exists( key) then
|
|||
|
|
skynet.server.redis:set( key , 10000000 * i )
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
|
|||
|
|
local key = string.format(redisKeyUrl.GameServerActivityID )
|
|||
|
|
if not skynet.server.redis:exists( key) then
|
|||
|
|
skynet.server.redis:set( key , 0 )
|
|||
|
|
end
|
|||
|
|
]]
|
|||
|
|
|
|||
|
|
--!!!!!!!!!!!!重要结束!!!!!!!!!!!!!!!!!!!!!
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--跨天
|
|||
|
|
function AccountServer:OnNewDay()
|
|||
|
|
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--1秒Timer
|
|||
|
|
function AccountServer:On1SecTimer()
|
|||
|
|
if not self:IsAccountServer( serverId ) then
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--5秒Timer
|
|||
|
|
function AccountServer:On5SecTimer()
|
|||
|
|
self:CheckOfflineUser()
|
|||
|
|
--self:IntervalSaveUser()
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--间隔保存用户信息
|
|||
|
|
function AccountServer:IntervalSaveUser()
|
|||
|
|
local playerList = skynet.server.playerCenter:GetPlayerList()
|
|||
|
|
local saveCount = 0
|
|||
|
|
for userId, value in pairs( playerList ) do
|
|||
|
|
if saveCount >= 5 then
|
|||
|
|
break
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if value.isIntervalSave and skynet.GetTime() - value.lastIntervalSaveTime >= 10 then
|
|||
|
|
log.info("玩家" ,userId, "五分钟保存一次数据")
|
|||
|
|
value.isIntervalSave = false
|
|||
|
|
if self:SavePlayerToDB( value.player , true ) then
|
|||
|
|
saveCount = saveCount +1
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--检查玩家是否超时未游戏
|
|||
|
|
function AccountServer:CheckOfflineUser()
|
|||
|
|
local playerList = skynet.server.playerCenter:GetPlayerList()
|
|||
|
|
local saveCount = 0
|
|||
|
|
|
|||
|
|
for userId, value in pairs( playerList ) do
|
|||
|
|
if saveCount >= self.PerSaveMaxCount then
|
|||
|
|
break
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--玩家ping超时,就设为掉线状态
|
|||
|
|
if 0 == skynet.AddTime and skynet.server.playerCenter.Status_Playing == value.status and 0 ~= value.pingTime and skynet.GetTime() - value.pingTime >= skynet.server.gameServer.curServerConfig.CheckPingTimeout then
|
|||
|
|
log.info(string.format("玩家 %d Ping超时 %d 强制让玩家掉线 " , userId , skynet.GetTime() - value.pingTime))
|
|||
|
|
skynet.server.playerCenter:UserOffline( userId )
|
|||
|
|
skynet.server.playerCenter:CloseSocket( value.socketId )
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if skynet.server.playerCenter.Status_Offline == value.status and 0 ~= value.offlineTime and skynet.GetTime() - value.offlineTime >= skynet.server.gameServer.curServerConfig.CheckOfflineUserTime then
|
|||
|
|
log.info("玩家" ,userId, "超时未游戏,已被服务器下线")
|
|||
|
|
if self:SavePlayerToDB( value.player , false ) then
|
|||
|
|
saveCount = saveCount +1
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--用户登陆游戏
|
|||
|
|
function AccountServer:UserLoginGame( c2sData , s2cData )
|
|||
|
|
local platform = c2sData.data.platform
|
|||
|
|
local account = c2sData.data.loginAccount
|
|||
|
|
local t1 = skynet.GetTime()
|
|||
|
|
|
|||
|
|
--检查是创建还是登陆
|
|||
|
|
local redisKey = string.format(redisKeyUrl.AccountServerUserList , platform , account )
|
|||
|
|
if not skynet.server.redis:exists( redisKey) then
|
|||
|
|
--不存在帐号,创建帐号
|
|||
|
|
self:UserCreateToDB( c2sData , s2cData )
|
|||
|
|
else
|
|||
|
|
--这里肯定是存在玩家帐号的
|
|||
|
|
local dbIndex = skynet.server.redis:hget( redisKey , "DBIndex")
|
|||
|
|
local userId = tonumber(skynet.server.redis:hget( redisKey , "UserID")) --从REDIS中取出来的数据会变成字符串
|
|||
|
|
log.info("玩家登陆游戏" , dbIndex , userId , platform , account )
|
|||
|
|
|
|||
|
|
--根据玩家是否在线来决定从哪里去取数据
|
|||
|
|
local isExistOnlinePlayer = skynet.server.playerCenter:IsExistPlayer( platform , account )
|
|||
|
|
if isExistOnlinePlayer then
|
|||
|
|
--直接从内存中加载数据
|
|||
|
|
self:LoadUserFromMem( account , userId , s2cData )
|
|||
|
|
else
|
|||
|
|
--玩家数据不在内存中,从DB中加载
|
|||
|
|
self:LoadUserFromDB( c2sData , s2cData , dbIndex , userId )
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if errorInfo.Suc == s2cData.code then
|
|||
|
|
--将该玩家的ID放入在线玩家列表中
|
|||
|
|
log.info("将玩家UserID 加入到在线列表成功 ",account )
|
|||
|
|
redisKey = string.format(redisKeyUrl.AccountServerOnlinePlayerList , c2sData.data.platform , serverId )
|
|||
|
|
skynet.server.redis:sadd( redisKey , account )
|
|||
|
|
s2cData.data.playerData = json:encode(s2cData.data.playerData)
|
|||
|
|
else
|
|||
|
|
log.info("将玩家UserID 加入到在线列表失败 ",account , s2cData.code)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--创建用户数据到DB中
|
|||
|
|
function AccountServer:UserCreateToDB( c2sData , s2cData )
|
|||
|
|
local platform = c2sData.data.platform
|
|||
|
|
local account = c2sData.data.loginAccount
|
|||
|
|
local version = c2sData.data.version
|
|||
|
|
s2cData.code = errorInfo.Suc
|
|||
|
|
s2cData.data = {}
|
|||
|
|
|
|||
|
|
--检查服务器是否能注册
|
|||
|
|
if not skynet.server.gameServer.curServerConfig.IsCanReg then
|
|||
|
|
s2cData.code = errorInfo.ErrorCode.ForbidUserReg
|
|||
|
|
return s2cData
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
local rand = 0
|
|||
|
|
local token = ""
|
|||
|
|
local sql = nil
|
|||
|
|
local queryData = nil
|
|||
|
|
local curId = nil
|
|||
|
|
|
|||
|
|
--分配DB索引
|
|||
|
|
local redisKey = string.format(redisKeyUrl.AccountServerCurDBIndex )
|
|||
|
|
local dbIndex = skynet.server.redis:incr(redisKey)
|
|||
|
|
|
|||
|
|
if dbIndex > skynet.server.gameConfig.DBInfoConfig.playerTableCount then
|
|||
|
|
skynet.server.redis:set(redisKey , 1)
|
|||
|
|
dbIndex = 1
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--获取最新DBID
|
|||
|
|
redisKey = string.format(redisKeyUrl.AccountServerUserDBID , dbIndex )
|
|||
|
|
local curId = skynet.server.redis:incr(redisKey)
|
|||
|
|
|
|||
|
|
--所有玩家列表到redis
|
|||
|
|
local keyUserList = string.format(redisKeyUrl.AccountServerUserList , platform , account )
|
|||
|
|
skynet.server.redis:hset( keyUserList , "DBIndex" ,dbIndex )
|
|||
|
|
skynet.server.redis:hset( keyUserList , "UserID" ,curId )
|
|||
|
|
|
|||
|
|
--所有玩家列表到DB
|
|||
|
|
sql = string.format(sqlUrl.insertUserDBIndexToUser , curId , platform , account , dbIndex )
|
|||
|
|
queryData = skynet.server.db:Query( "account" , sql )
|
|||
|
|
if 1 ~= queryData.affected_rows then
|
|||
|
|
log.info("插入DBIndex失败",skynet.server.common:TableToString(queryData) ,sql)
|
|||
|
|
s2cData.code = errorInfo.ErrorCode.AlreadyCreateAccount
|
|||
|
|
log.info("创建user数据失败,删除redis中的key ",keyUserList)
|
|||
|
|
skynet.server.redis:del(keyUserList)
|
|||
|
|
return s2cData
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--组装玩家的游戏数据
|
|||
|
|
local newPlayer = {}
|
|||
|
|
newPlayer.basicInfo = {}
|
|||
|
|
dbData:Traversal( newPlayer.basicInfo , playerFields.InitBasicInfo )
|
|||
|
|
newPlayer.basicInfo.userID = tonumber(curId)
|
|||
|
|
newPlayer.basicInfo.platform = platform
|
|||
|
|
newPlayer.basicInfo.accountName = account
|
|||
|
|
newPlayer.basicInfo.appVersion = c2sData.data.version
|
|||
|
|
newPlayer.basicInfo.channel = c2sData.data.channel
|
|||
|
|
newPlayer.basicInfo.regTime = skynet.GetTime()
|
|||
|
|
newPlayer.basicInfo.regIP = c2sData.addr
|
|||
|
|
newPlayer.basicInfo.lastLoginIP = c2sData.addr
|
|||
|
|
newPlayer.basicInfo.lastLoginTime = skynet.GetTime()
|
|||
|
|
newPlayer.basicInfo.lastExitTime = 0
|
|||
|
|
|
|||
|
|
--游戏数据
|
|||
|
|
newPlayer.gameData = {}
|
|||
|
|
|
|||
|
|
--非存档用户可以初始化线上数据
|
|||
|
|
dbData:Traversal( newPlayer.gameData , playerFields.InitGameData )
|
|||
|
|
|
|||
|
|
--存档数据
|
|||
|
|
newPlayer.archiveData = "{}"
|
|||
|
|
--在player表中创建玩家的数据
|
|||
|
|
sql = string.format(sqlUrl.insertAccountToPlayer ,curId , account , platform , json:encode( newPlayer.basicInfo ) , json:encode( newPlayer.gameData ) , newPlayer.archiveData )
|
|||
|
|
queryData = skynet.server.db:QueryPlayer( dbIndex , sql )
|
|||
|
|
if 1 ~= queryData.affected_rows then
|
|||
|
|
log.info("未成功创建用户ID信息 ",account)
|
|||
|
|
log.info("失败SQL ",sql)
|
|||
|
|
log.info("创建player数据失败,删除user中的玩家ID",curId)
|
|||
|
|
sql = string.format(sqlUrl.deleteUserDBIndexToUser , curId)
|
|||
|
|
skynet.server.db:Query( "account" , sql )
|
|||
|
|
log.info("创建player数据失败,删除redis中的key",keyUserList)
|
|||
|
|
skynet.server.redis:del(keyUserList)
|
|||
|
|
s2cData.code = errorInfo.ErrorCode.AlreadyCreateAccount
|
|||
|
|
return s2cData
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--创建后再进入游戏
|
|||
|
|
log.info("创建帐号并登陆成功 用户ID",curId,account)
|
|||
|
|
|
|||
|
|
local userData = {}
|
|||
|
|
userData.userId = curId
|
|||
|
|
userData.account = account
|
|||
|
|
userData.playerData = newPlayer
|
|||
|
|
userData.announce = ""
|
|||
|
|
s2cData.data = userData
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--从内存中加载玩家数据
|
|||
|
|
function AccountServer:LoadUserFromMem( account , userId , s2cData )
|
|||
|
|
local player = skynet.server.playerCenter:GetPlayer( userId )
|
|||
|
|
if not player then
|
|||
|
|
log.info(string.format("玩家 %d 从内存中加载失败",userId ))
|
|||
|
|
s2cData.code = errorInfo.ErrorCode.NoExistUser
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
local playerData = {}
|
|||
|
|
playerData.basicInfo = player.basicInfo
|
|||
|
|
playerData.gameData = player.gameData
|
|||
|
|
playerData.archiveData = player.archiveData
|
|||
|
|
|
|||
|
|
--用户数据
|
|||
|
|
local userData = {}
|
|||
|
|
userData.userId = userId
|
|||
|
|
userData.account = account
|
|||
|
|
userData.playerData = playerData
|
|||
|
|
userData.announce = ""
|
|||
|
|
s2cData.data = userData
|
|||
|
|
log.info(string.format("玩家 %d 从内存中加载数据",userId))
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--从DB中加载玩家数据
|
|||
|
|
function AccountServer:LoadUserFromDB( c2sData , s2cData , dbIndex , userId )
|
|||
|
|
local platform = c2sData.data.platform
|
|||
|
|
local account = c2sData.data.loginAccount
|
|||
|
|
|
|||
|
|
--从玩家表中获取数据
|
|||
|
|
local sql = string.format(sqlUrl.queryAccountFromPlayer , userId )
|
|||
|
|
local queryData = skynet.server.db:QueryPlayer( dbIndex , sql )
|
|||
|
|
queryData = queryData[1]
|
|||
|
|
|
|||
|
|
local basicInfo = json:decode(queryData.BasicInfo)
|
|||
|
|
local gameData = json:decode(queryData.GameData)
|
|||
|
|
local archiveData = queryData.ArchiveData
|
|||
|
|
|
|||
|
|
log.info(string.format("玩家 %s 平台 %s BasicInfo %s GameData %s", account , platform , queryData.BasicInfo , queryData.GameData))
|
|||
|
|
|
|||
|
|
if defense.AccountStatus_Black == basicInfo.Status then
|
|||
|
|
log.info("玩家",userId ,"已经被列入黑名单,无法登陆游戏")
|
|||
|
|
s2cData.code =errorInfo.ErrorCode.BlackList
|
|||
|
|
return s2cData
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--检查是否有新字段,有新字段进行处理
|
|||
|
|
dbData:CheckNewFields( basicInfo , playerFields.InitBasicInfo)
|
|||
|
|
dbData:CheckNewFields( gameData , playerFields.InitGameData)
|
|||
|
|
|
|||
|
|
local playerData = {}
|
|||
|
|
playerData.basicInfo = basicInfo
|
|||
|
|
playerData.gameData = gameData
|
|||
|
|
playerData.archiveData = archiveData
|
|||
|
|
|
|||
|
|
--处理一下邮件的转义问题
|
|||
|
|
for k ,v in pairs(playerData.gameData.mail.mailList) do
|
|||
|
|
v.content = string.gsub(v.content,"\n","\\n")
|
|||
|
|
v.content = string.gsub(v.content,"\r","\\r")
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--用户数据
|
|||
|
|
local userData = {}
|
|||
|
|
userData.userId = userId
|
|||
|
|
userData.account = account
|
|||
|
|
userData.playerData = playerData
|
|||
|
|
userData.announce = ""
|
|||
|
|
s2cData.data = userData
|
|||
|
|
log.info(string.format("玩家 %d 从DB中加载数据",userId))
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--同步玩家数据到Redis
|
|||
|
|
function AccountServer:SyncPlayerDataToRedis( playerData , redisKey )
|
|||
|
|
local basickey = ""
|
|||
|
|
if not redisKey then
|
|||
|
|
basickey = string.format(redisKeyUrl.AccountServerOnlinePlayer , playerData.basicInfo.platform , playerData.basicInfo.userID )
|
|||
|
|
else
|
|||
|
|
basickey = string.format("%s" , redisKey)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
for k, v in pairs( playerData ) do
|
|||
|
|
if "table" == type( v ) then
|
|||
|
|
self:SyncPlayerDataToRedis( v , string.format("%s:%s" , basickey , k ))
|
|||
|
|
else
|
|||
|
|
skynet.server.redis:hset(basickey,k,v)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--从Redis获取玩家数据
|
|||
|
|
function AccountServer:GetPlayerFromRedis( id , redisKeyUrl , initData ,data )
|
|||
|
|
if -1 == id then
|
|||
|
|
return
|
|||
|
|
end
|
|||
|
|
for k, v in pairs( initData ) do
|
|||
|
|
if "table" == type(v[2]) then
|
|||
|
|
redisKeyUrl = string.format("%s:%s" , redisKeyUrl , v[1] )
|
|||
|
|
data[ v[1] ] = {}
|
|||
|
|
self:GetPlayerFromRedis( id , redisKeyUrl , v[2] , data[ v[1] ] )
|
|||
|
|
else
|
|||
|
|
data[ v[1] ] = skynet.server.redis:hget(redisKeyUrl, v[1] )
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--从Redis中删除玩家数据
|
|||
|
|
function AccountServer:DeletePlayerDataFromRedis( userId , redisKeyUrl , initData )
|
|||
|
|
for k, v in pairs( initData ) do
|
|||
|
|
if "table" == type(v[2]) then
|
|||
|
|
redisKeyUrl = string.format("%s:%s" , redisKeyUrl , v[1] )
|
|||
|
|
self:DeletePlayerDataFromRedis( userId , redisKeyUrl , v[2] )
|
|||
|
|
else
|
|||
|
|
local fields = skynet.server.redis:hkeys(redisKeyUrl)
|
|||
|
|
for k, v in pairs(fields) do
|
|||
|
|
skynet.server.redis:hdel(redisKeyUrl , v)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--保存玩家数据到DB
|
|||
|
|
function AccountServer:SavePlayerToDB( player , isIntervalSave )
|
|||
|
|
function Do()
|
|||
|
|
local platform = player.basicInfo.platform
|
|||
|
|
local account = player.basicInfo.accountName
|
|||
|
|
local userId = player.basicInfo.userID
|
|||
|
|
|
|||
|
|
local basicInfo = player.basicInfo
|
|||
|
|
local gameData = player.gameData
|
|||
|
|
local archiveData =player.archiveData
|
|||
|
|
|
|||
|
|
--计算在线时间
|
|||
|
|
basicInfo.allGameTime = basicInfo.allGameTime + (os.time() - player.tmpData.intoTime)
|
|||
|
|
|
|||
|
|
--获取数据库索引
|
|||
|
|
local redisKey = string.format(redisKeyUrl.AccountServerUserList , platform , account )
|
|||
|
|
local dbIndex = skynet.server.redis:hget( redisKey , "DBIndex")
|
|||
|
|
|
|||
|
|
--更新玩家所有数据到数据库
|
|||
|
|
local sql = string.format(sqlUrl.saveAccountToPlayer , json:encode(basicInfo) , json:encode(gameData) , archiveData , userId )
|
|||
|
|
skynet.server.db:QueryPlayer( dbIndex , sql )
|
|||
|
|
log.info(string.format("玩家 %d 成功保存数据到DB 数据库索引 %d ", userId , dbIndex ))
|
|||
|
|
|
|||
|
|
--不是间隔保存才会剔除玩家
|
|||
|
|
if not isIntervalSave then
|
|||
|
|
--让玩家从在线列表中删除
|
|||
|
|
redisKey = string.format(redisKeyUrl.AccountServerOnlinePlayerList , platform , serverId )
|
|||
|
|
skynet.server.redis:srem( redisKey , account )
|
|||
|
|
skynet.server.playerCenter:UserExit( userId )
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
local ret,err = pcall(Do)
|
|||
|
|
if not ret then
|
|||
|
|
log.info("内部错误信息 保存当前玩家失败",player.basicInfo.userID ,ret ,err)
|
|||
|
|
return false
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
return true
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
skynet.server.accountServer = AccountServer
|
|||
|
|
return AccountServer
|