HomeServer/Server/Common/Player/PlayerCenter.lua
2024-11-20 15:41:37 +08:00

638 lines
24 KiB
Lua
Raw Permalink 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 mysql = require "skynet.db.mysql"
local oo = require "Class"
local cmd = require "GameCmd"
local json =require "json"
local player = require "Player"
local log = require "Log"
local pb = require "pb"
local sqlUrl = require "SqlUrl"
local playerFields = require "PlayerFields"
local redisKeyUrl = require "RedisKeyUrl"
local dyeworkshop = require "DyeWorkShop"
local errorInfo = require "ErrorInfo"
local dataType = require "DataType"
local cluserServer = require "ClusterServer"
local serverId = tonumber(skynet.getenv "serverId")
local PlayerCenter = oo.class()
--[[
大致修改 登陆时检查下是否有新字段然后同步的REDIS中
]]
PlayerCenter.Status_Login = 1 --用户已登陆
PlayerCenter.Status_Playing = 2 --用户游戏中
PlayerCenter.Status_Offline = 3 --用户掉线
function PlayerCenter:Init()
self.redisKeyPlaying = string.format( redisKeyUrl.GameServerPlayingInServerZSet , serverId ) --玩家正在当前游戏服的redis key
self.playerList = {}
self.listAuditUser = {} --审核玩家列表
self.listPreCloseSocket = {} --准备关闭SocketId
--table.insert( self.listAuditUser , "xxx")
end
--跨天
function PlayerCenter:OnNewDay()
for k, v in pairs(self.playerList) do
function Do()
--非存档玩家才能走这里
if self.Status_Playing == v.status then
v.player:ResetTodayData()
--判断是否是新的一周或一月
if skynet.server.common:IsNewWeek(v.player.basicInfo.lastLoginTime) then
v.player:ResetWeekData()
end
if skynet.server.common:IsNewMonth(v.player.basicInfo.lastLoginTime) then
v.player:ResetMonthData()
end
v.player.basicInfo.lastLoginTime = skynet.GetTime()
end
--主动推送当前活动信息
local data = {}
data.activityInfos = skynet.server.activity:GetAllActivityInfo( v.player )
skynet.server.gameServer:SendMsgToUser( v.player.userId , "CMD_S2C_UpdateShowUI" , data )
--检查一下是否有新的活动邮件需要发送
skynet.server.activity:ActivityExpiredSendMail(v.player)
return true
end
local ret,err = pcall(Do)
if not ret or not err then
local account = v.player.basicInfo.accountName
local userId = player.userId
log.info("内部错误信息 PlayerCenter:OnNewDay", account ,userId ,ret ,err )
return false
end
end
end
--每5秒调一次
function PlayerCenter:On5SecTimer()
if not cluserServer:IsGameServer(serverId) then
return
end
local nowTime = skynet.GetTime()
--被顶号的sokcet超过5秒就关闭一下
for k, v in pairs( self.listPreCloseSocket ) do
if nowTime - v.recordTime >= 5 then
self:CloseSocket( v.netType , v.socketId , v.agentId )
self.listPreCloseSocket[ k ] = nil
end
end
end
--关闭SocketId连接
function PlayerCenter:CloseSocket( netType , socketId , agentId )
function Do()
local s2cData,retLen = nil , nil
if dataType.NetType_Tcp == netType then
s2cData,retLen = skynet.call( "TcpSocket", "lua","KickUserOffline", socketId )
elseif agentId and dataType.NetType_WebSocket == netType then
s2cData,retLen = skynet.call( "WebSocketAgent", "lua","KickUserOffline", socketId , agentId )
end
if errorInfo.Suc == s2cData.code then
log.info(string.format("关闭 网络类型 %d 连接 socketId %d 成功" , netType , socketId ))
return true
else
log.info(string.format("关闭 网络类型 %d 连接 socketId %d 失败" , netType , socketId ))
return false
end
return true
end
local ret,err = pcall(Do)
if not ret or not err then
log.info("内部错误信息 PlayerCenter:CloseSocket", netType ,socketId , agentId , ret ,err )
return false
end
return true
end
--是否存在游戏玩家查找帐号
function PlayerCenter:IsExistPlayerForAccount( account )
account = tostring(account)
for k, v in pairs( self.playerList ) do
if account == v.account then
return true
end
end
return false
end
--是否存在游戏玩家
function PlayerCenter:IsExistPlayer( account )
for k, v in pairs( self.playerList ) do
if account == v.account then
return true
end
end
return false
end
--根据account判断玩家是否在线
function PlayerCenter:IsPlayingByAccount( account )
for k, v in pairs( self.playerList ) do
if account == v.account and v.status == self.Status_Playing then
--log.debug(string.format("玩家 %s 在线,data:%s" , account ,json:encode(v)))
return true
end
end
return false
end
--玩家是否在线
function PlayerCenter:IsPlaying( userId )
if self.playerList[ userId ] and self.Status_Playing == self.playerList[ userId ].status then
return true
end
return false
end
--是否掉线
function PlayerCenter:IsOffline( socketId )
if self.playerList[ socketId ] and self.Status_Offline == self.playerList[ socketId ].status then
return true
end
return false
end
--根据帐号获取SocketId
function PlayerCenter:GetSocketIdForAccount( account )
for k, v in pairs(self.playerList) do
if account == v.account then
return k
end
end
return -1
end
--根据userId获取SocketId
function PlayerCenter:GetSocketIdForUserID( userId )
if self.playerList[ userId ] then
return self.playerList[ userId ].netType , self.playerList[ userId ].socketId , self.playerList[ userId ].agentId
end
return -1,-1
end
--根据SocketId获取userId
function PlayerCenter:GetUserIDForSocketId( netType , socketId )
for k, v in pairs(self.playerList) do
if netType == v.netType and socketId == v.socketId then
return v.userId
end
end
return nil
end
--获取人数信息
function PlayerCenter:GetPlayerCount()
local countInfo = {}
countInfo.playing = 0
countInfo.offline = 0
for k, v in pairs(self.playerList) do
if self.Status_Playing == v.status then
countInfo.playing = countInfo.playing + 1
elseif self.Status_Offline == v.status then
countInfo.offline = countInfo.offline + 1
end
end
return countInfo
end
--进入平台
function PlayerCenter:UserEnter( c2sData , playerData )
local netType = c2sData.netType
local socketId = c2sData.socketId
local addr = c2sData.addr
local platform = c2sData.data.platform
local curHardwareCode = tostring(c2sData.data.hardwareCode)
local userId = tonumber(playerData.basicInfo.userID)
local account = playerData.basicInfo.accountName
playerData.basicInfo.userID = userId --老版本的还是字符串,这里强制改成数字型
playerData.basicInfo.isAudit = self:IsAudit( account ) --是否为审核帐号
local cfgSValue = skynet.server.gameConfig:GetPlayerAllCfg( playerData , "SValue")
local nowTime = skynet.GetTime()
if self.playerList[ userId ] then
--关闭其它Tcp连接
local oldHardwareCode = tostring( self.playerList[ userId ].player.basicInfo.hardwareCode )
local oldNetType = self.playerList[ userId ].netType
local oldSocketId = self.playerList[ userId ].socketId
local oldAgentId = self.playerList[ userId ].agentId
if oldHardwareCode ~= curHardwareCode and "" ~= oldHardwareCode and "" ~= curHardwareCode then
skynet.server.gameServer:SendMsgToUser( userId , "CMD_S2C_UserExit" , { exitType = 2 })
table.insert( self.listPreCloseSocket , { netType = oldNetType , socketId = oldSocketId , agentId = oldAgentId , recordTime = nowTime } )
log.info(string.format("玩家 %d 登陆设备信息不一致 开始顶号 原设备 %s 当前设备码 %s" ,userId , oldHardwareCode , curHardwareCode))
else
self:CloseSocket( oldNetType , oldSocketId , oldAgentId )
end
--self:CloseSocket( oldNetType , oldSocketId , oldAgentId )
log.info(string.format("玩家 %d socket %d 帐号 %s 重新进入游戏 关闭原 socket %d 当前设备码 %s" , userId , socketId , account , oldSocketId , curHardwareCode))
self.playerList[ userId ].player.basicInfo.hardwareCode = curHardwareCode
--计算在线时间
self.playerList[ userId ].player:CalcGameTime( nowTime )
--如果是同一天 则增加当天登录时间
if skynet.server.common:IsSameDay(self.playerList[ userId ].player.basicInfo.lastGameTime , nowTime) then
--这里提前保存下agentId,因为商城要发送消息给客户端,这时玩家新的客房端已经更新了agentId , 如果不更新的化,会找不到对应的玩家.
self.playerList[ userId ].netType = netType
self.playerList[ userId ].socketId = socketId
if dataType.NetType_WebSocket == netType then
self.playerList[ userId ].agentId = c2sData.agentId
end
--判断是否可以触发相关礼包
local packInfo = cfgSValue.triggerUpgradePack
if self.playerList[ userId ].player.gameData.todayGain.todayGameTime >= packInfo[1]*60*60
and nowTime - self.playerList[ userId ].player.gameData.upTime >= packInfo[1]*60*60
and self.playerList[ userId ].player.gameData.level < self.playerList[ userId ].player.maxLevelLimt
and self.playerList[ userId ].player.gameData.level > cfgSValue.storeUnlockLvl then
skynet.server.store:TriggerPack(self.playerList[ userId ].player , skynet.server.store.TriggerPack_UpGrade)
end
end
else
playerData.basicInfo.hardwareCode = curHardwareCode
local newPlayer = player.new( userId , playerData )
self.playerList[ userId ] = {}
self.playerList[ userId ].player = newPlayer
--将玩家的账号加入到当前服的在线列表中
skynet.server.redis:zadd( self.redisKeyPlaying , nowTime , account )
log.info(string.format("玩家 %d socket %d 帐号 %s 进入游戏 设备码 %s" , userId , socketId , account , "" == curHardwareCode and "暂无" or curHardwareCode))
end
self.playerList[ userId ].netType = netType
if dataType.NetType_WebSocket == netType then
self.playerList[ userId ].agentId = c2sData.agentId --websocket需要保存下代理的ID
end
self.playerList[ userId ].socketId = socketId
self.playerList[ userId ].platform = platform
self.playerList[ userId ].userId = userId
self.playerList[ userId ].account = account
self.playerList[ userId ].addr = addr
self.playerList[ userId ].status = self.Status_Playing
self.playerList[ userId ].offlineTime = 0
self.playerList[ userId ].pingTime = nowTime
self.playerList[ userId ].lastSaveTime = nowTime --上一次间隔保存
self.playerList[ userId ].onlineMailList = {} --在线发送的邮件列表
self.playerList[ userId ].saveMysqlCount = 0 --mysql保存次数
self.playerList[ userId ].player:Init( c2sData )
local player = self.playerList[ userId ].player
local partnerId = player.gameData.partner.id
skynet.server.personal:SetDetail( partnerId ,"isOnline", true , "gameServerId", serverId )
--检查是否有离线消息
skynet.server.personal:CheckOfflineMsg( player )
--检查是否有失败的充值信息
skynet.server.gameServer:CheckFailedPay( player )
--检查是否有失败的官方充值信息
skynet.server.gameServer:CheckOfficialFailedPay( player )
--检查是否有失败的 微信通知 信息
skynet.server.gameServer:CheckWXNotifyFailedPay( player )
end
--退出平台
function PlayerCenter:UserExit( userId )
local netType = self.playerList[ userId ].netType
local socketId = self.playerList[ userId ].socketId
local account = self.playerList[ userId ].account
local agentId = self.playerList[ userId ].agentId
if self:CloseSocket( netType , socketId , agentId ) then
log.info(string.format("玩家 %d socket %d 帐号 %s 退出游戏" , userId , socketId , account))
else
log.info(string.format("玩家 %d socket %d 帐号 %s 退出游戏 未成功关闭socket" , userId , socketId , account))
end
--将玩家的账号移除到当前服的在线列表中
skynet.server.redis:zrem( self.redisKeyPlaying , account )
skynet.server.playerRecord:Add( userId , dataType.RecordType_3 , 0 )
self.playerList[ userId ].player = nil
self.playerList[ userId ]= nil
return true
end
--Ping时间
function PlayerCenter:Ping( userId )
self.playerList[ userId ].pingTime = skynet.GetTime()
end
--掉线
function PlayerCenter:UserOffline( userId )
if not self.playerList[ userId ] then
return
end
self.playerList[ userId ].status = self.Status_Offline
self.playerList[ userId ].offlineTime = skynet.GetTime()
local player = self.playerList[ userId ].player
local partnerId = player.gameData.partner.id
skynet.server.personal:SetDetail( partnerId ,"isOnline", false , "gameServerId", 0 )
skynet.server.playerRecord:Add( userId , dataType.RecordType_2 , 0 )
log.info(string.format("玩家 %d 掉线" , userId ))
--触发离线订阅
skynet.server.subscribe:UpdateOfflineTime(player,1)
end
--获取玩家数据
function PlayerCenter:Get( userId )
if not self.playerList[ userId ] then
return nil
end
return self.playerList[ userId ]
end
--获取玩家数据
function PlayerCenter:GetPlayer( userId )
userId = tonumber( userId )
if not self.playerList[ userId ] then
return nil
end
return self.playerList[ userId ].player
end
--获取玩家数据
function PlayerCenter:GetPlayerForAccount( account )
for k, v in pairs( self.playerList ) do
if account == v.account then
return v.player
end
end
return nil
end
--获取玩家数据列表
function PlayerCenter:GetPlayerList()
return self.playerList
end
--获取当前玩家数量
function PlayerCenter:GetPlayerOnlineCount()
return #self.playerList
end
--获取玩家登陆数据
function PlayerCenter:GetLoginInfo( userId )
if not self.playerList[ userId ] then
return nil
end
local userData = {}
local player = self.playerList[ userId ].player
--每次登录更新下我的时间
local redisKey = redisKeyUrl.GameServerPartnerAllUserZSet
local myPartnerId = player.gameData.partner.id
skynet.server.redis:zadd( redisKey , skynet.GetTime() , myPartnerId )
--用户数据
userData.userId = tonumber(player.basicInfo.userID)
userData.account = player.basicInfo.accountName
userData.playerData = nil
userData.announce = ""
userData.serverTime = skynet.GetTime()
userData.msgVersion = 2
userData.nickName = player.gameData.nickName
userData.partnerId = myPartnerId
local curDetail = skynet.server.personal:GetDetail( myPartnerId )
if not curDetail then
player.gameData.partner.id = "XJtuaqxodoDf"
myPartnerId = player.gameData.partner.id
curDetail = skynet.server.personal:GetDetail( myPartnerId ) --内网测试ID 由于从线上拷贝的数据没有partnerid只能默认一个内网有的partnerid
if not curDetail then
player.gameData.partner.id = "AACJQUquNsJM"
myPartnerId = player.gameData.partner.id
curDetail = skynet.server.personal:GetDetail( myPartnerId ) --内网测试ID 由于从线上拷贝的数据没有partnerid只能默认一个内网有的partnerid
end
if not curDetail then
player.gameData.partner.id = "cMqfgHeQCxbN"
myPartnerId = player.gameData.partner.id
curDetail = skynet.server.personal:GetDetail( myPartnerId ) --内网测试ID 由于从线上拷贝的数据没有partnerid只能默认一个内网有的partnerid
end
--内网3
if not curDetail then
player.gameData.partner.id = "TsnQYmRAxfcy"
myPartnerId = player.gameData.partner.id
curDetail = skynet.server.personal:GetDetail( myPartnerId ) --内网测试ID 由于从线上拷贝的数据没有partnerid只能默认一个内网有的partnerid
end
end
--兼容redis数据
player:InitNoRedisData( player , curDetail )
local myGroupId = curDetail.groupId
local groupData = skynet.server.group:GetGroupInfo( myGroupId , "contributeReward", "joinType")
local money = {}
table.insert( money , { type = pb.enum("EnumGoodsType","Coin") , count = player.gameData.coin })
table.insert( money , { type = pb.enum("EnumGoodsType","Clovers") , count = player.gameData.clovers })
table.insert( money , { type = pb.enum("EnumGoodsType","VoluteCoin") , count = player.gameData.volute })
table.insert( money , { type = pb.enum("EnumGoodsType","ContributeCoin") , count = player.gameData.contributeCoin })
userData.money = money
userData.level = player.gameData.level
userData.exp = player.gameData.exp
--当前房子ID和方案ID
local houseId = player.gameData.curHouseID
userData.curHouseID = houseId
userData.curSchemeID = player.gameData.house[ houseId ].curSchemeId
--获取方案信息
userData.schemeInfo = {}
for k, v in pairs( player.gameData.house[ houseId ].scheme ) do
table.insert( userData.schemeInfo , { id = v.id , name = v.name , status = v.status })
end
--当前房子和当前方案下的家具信息
userData.furnitureInfo = skynet.server.house:GetCurSchemeFurniture( player , houseId , userData.curSchemeID )
local tmpDecorateInfo = skynet.server.house:GetCurSchemeDecorate( player , houseId , userData.curSchemeID )
userData.decorateInfo = {}
--装修时摆放了类型10的户外装饰后面又取消了在LUA表中没有10这个索引所以后面的值没传过来这里按这种方式重新赋值
if tmpDecorateInfo then
for k, v in pairs( tmpDecorateInfo ) do
table.insert( userData.decorateInfo , v )
end
end
--当前已经解锁的区域ID
userData.areaId = {}
for areaId, v in pairs( player.gameData.house[ houseId ].unlockAreaList ) do
if true == v then
table.insert( userData.areaId , areaId )
end
end
--提示信息
userData.tipsInfo = skynet.server.msgTips:GetAllTips( player , curDetail , groupData )
userData.finishGuide = player.gameData.finishGuide --完成的引导记录
--将未解锁的宠物发给玩家
userData.lockPetId = {}
for k, v in pairs( player.gameData.pet ) do
if dataType.PetStatus_Lock == v.status then
table.insert( userData.lockPetId , v.skinId )
end
end
--获取拥有的房子数量
userData.houseCount = skynet.server.house:GetHouseCount( player )
userData.isVip = {}
if userData.passCheck then
table.insert(userData.isVip ,userData.passCheck.isVip )
else
table.insert(userData.isVip , false)
end
userData.goodsStat = {}
local goodsType = dataType.GoodsType_Furniture
table.insert( userData.goodsStat , { goodsType = goodsType , goodsCount = skynet.server.bag:GetGoodsStat( player , goodsType )})
userData.allGameTime = player.basicInfo.allGameTime
--头像 若头像id=0则带上自定义头像信息
if player.gameData.personal.headId == 0 then
if player.gameData.personal.diyHeadInfo == nil or next(player.gameData.personal.diyHeadInfo) == nil then
player.gameData.personal.headId = 1
else
userData.DIYHeadInfo = player.gameData.personal.diyHeadInfo
end
end
userData.currentHeadId = player.gameData.personal.headId
userData.currentHpFrameId = player.gameData.personal.headFrameId
if curDetail and "" == curDetail.groupId then
userData.isJoinGroup = false
player:SetSwitch( dataType.SwitchType_JoinGroup , false )
else
userData.isJoinGroup = true
player:SetSwitch( dataType.SwitchType_JoinGroup , true )
end
-- 到达18级后给给鱼竿鱼饵数据赋值
if player.gameData.shop[dataType.ShopType_Fish] then
for k,v in pairs(player.gameData.shop[dataType.ShopType_Fish].fishingRodInfos) do
if v.status == 2 then
userData.fishRod = v.id -- 当前装备的鱼竿
end
end
userData.fishLures = player.gameData.shop[dataType.ShopType_Fish].fishingLure --当前拥有的鱼饵
end
--宠物状态
userData.petStatus = {}
for k, v in pairs( player.gameData.pet ) do
table.insert( userData.petStatus , { petType = k , petStatus = v.status })
end
--可显示活动的相关数据
userData.activityInfos = skynet.server.activity:GetAllActivityInfo( player )
userData.configType = player.basicInfo.configType
userData.switches = player.gameData.switches --所有开关信息
userData.adInfos = skynet.server.ad:GetAllAD( player ) --所有广告信息
--需要进行拍屏的礼包
userData.popupStoreInfos = {}
if player.gameData.storePack.popupPackInfo then
for k , v in pairs(player.gameData.storePack.popupPackInfo) do
if v.status == 1 then
--单独处理下月卡的拍屏
if v.id == 61 then
table.insert(userData.popupStoreInfos , {storeId = v.id , buyTimes = 0 , failureTime = 0 , redDot = false})
else
local packInfo = player.gameData.storePack.storePackInfo[ v.id ]
--限购礼包购买完后也不进行拍屏
if packInfo and packInfo.failureTime ~= nil and packInfo.failureTime ~= "" and packInfo.buyTimes < packInfo.packLimit[2] and skynet.GetTime() < packInfo.failureTime then
table.insert(userData.popupStoreInfos , {storeId = v.id , buyTimes = packInfo.buyTimes , failureTime = packInfo.failureTime , redDot = false})
elseif packInfo and not packInfo.failureTime and packInfo.buyTimes < packInfo.packLimit[2] then
--礼包对应的失效时间
local endTime = skynet.server.common:GetTime(skynet.server.gameConfig:GetPlayerCurCfg( player , "StorePack" , v.id).endTime)
if skynet.GetTime() < endTime then
table.insert(userData.popupStoreInfos , {storeId = v.id , buyTimes = packInfo.buyTimes , failureTime = endTime , redDot = false})
end
end
end
end
end
end
--需要进行礼包发奖表现补发的礼包id
userData.storePackIds = {}
if player.tmpData.sendStorePackIds then
userData.storePackIds = player.tmpData.sendStorePackIds
end
--微信订阅信息
userData.subscribeInfo=skynet.server.subscribe:GetPlayerSubscribeInfo(player,1)
--是否有官网离线充值信息
if next(player.tmpData.officialPayDatas) then
userData.officialPayInfos=player.tmpData.officialPayDatas
--清除数据
player.tmpData.officialPayDatas = {}
end
if userData.level>=3 then
userData.isDialogShow = skynet.server.birthday:CheckUnlockNpcDialog( player )
end
return userData
end
--是否为相同服务器
function PlayerCenter:IsSameServer( otherPartnerId )
local playerInfo = skynet.server.personal:GetDetail( otherPartnerId )
if playerInfo and playerInfo.isOnline and playerInfo.gameServerId then
if serverId == playerInfo.gameServerId then
return true
else
return false
end
else
return nil
end
end
--是否为审核玩家
function PlayerCenter:IsAudit( account )
for k, v in pairs( self.listAuditUser ) do
if account == v then
return true
end
end
return false
end
skynet.server.playerCenter = PlayerCenter
return PlayerCenter