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