HomeServer/lualib-src/Server-main/AllServer/GameServer/AccountServer.lua
2024-11-20 15:41:37 +08:00

417 lines
15 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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