HomeServer/lualib-src/Server-main/AllServer/GameServer/AccountServer.lua

417 lines
15 KiB
Lua
Raw Normal View History

2024-11-20 15:41:09 +08:00
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