HomeServer/Server/AllServer/RankServer/RankDecoWeek.lua

485 lines
23 KiB
Lua
Raw Permalink Normal View History

2024-11-20 15:41:09 +08:00
local skynet = require "skynet"
local oo = require "Class"
local log = require "Log"
local pb = require "pb"
local dataType = require "DataType"
local errorInfo = require "ErrorInfo"
local activity = require "Activity"
local sqlUrl = require "SqlUrl"
local db = require "DB"
local json =require "json"
local redisKeyUrl = require "RedisKeyUrl"
local personal = require "Personal"
local playerLand = require "PlayerLand"
local serverId = tonumber(skynet.getenv "serverId")
local RankDecoWeek = oo.class()
RankDecoWeek.ActivityType = dataType.ActivityType_DecoWeek
RankDecoWeek.ActivityStatus_No = 0 --无状态
RankDecoWeek.ActivityStatus_Join = 1 --参赛阶段
RankDecoWeek.ActivityStatus_Vote = 2 --评选阶段
RankDecoWeek.ActivityStatus_Balance = 3 --结算阶段
--[[
18:00:00 23:59:59
0:00:00 15:59:59
16:00:00 17:59:59
稿
NPC作品刷新次数不计入
-
]]
function RankDecoWeek:Init()
self.curActivityStatus = self.ActivityStatus_No --当前服的状态
end
--5秒Timer
function RankDecoWeek:On5SecTimer()
local activityThemeId , activityStatus , startTime , endTime = self:RefreshActivityInfo()
log.info(string.format("装修周赛 获取最新活动详情 期数 %d 活动状态 %d 开始时间 %s 结束时间 %s" , activityThemeId , activityStatus ,
skynet.server.common:GetStrTime(startTime) , skynet.server.common:GetStrTime(endTime)))
end
--重置活动数据
function RankDecoWeek:ResetActivityData( activityThemeId )
local redisKey = string.format( redisKeyUrl.GameServerActivityDecoWeekInfoHash )
local returnValue = skynet.server.redis:hsetnx( redisKey , "isResetActivityData" , true )
if 0 == returnValue then
--返回0表示当期已经处理过就不再往下走
return
end
--清除所有曝光数据
redisKey = string.format( redisKeyUrl.GameServerActivityDecoWeekInfoHash )
skynet.server.redis:hset( redisKey , "allExposureCount" , 0 )
skynet.server.redis:hdel( redisKey , "isGiveRankReward" ) --新的一期开始,清除之前的发奖的标记
skynet.server.redis:hdel( redisKey , "isCheckExtraExposure") --新的一期开始,清除之前的增加曝光次数的标记
skynet.server.redis:hset( redisKey , "allVoteScore" , 0 )
--清除投票介入分数
redisKey = redisKeyUrl.GameServerActivityDecoWeekHumanVoteAdjustScoreZSet
skynet.server.redis:del(redisKey)
--清除需要曝光次数
redisKey = redisKeyUrl.GameServerActivityDecoWeekNeedExpouseCountZSet
skynet.server.redis:del(redisKey)
--清除平均曝光次数
redisKey = redisKeyUrl.GameServerActivityDecoWeekAveExpouseCountZSet
skynet.server.redis:del(redisKey)
--初始化NPC的分数和设计时间
redisKey = redisKeyUrl.GameServerActivityDecoWeekNpcVoteScoreZSet
local cfgAllNPCName = skynet.server.gameConfig:GetAllCfg( "NPCName")
for k, v in pairs( cfgAllNPCName ) do
local robotPartnerId = string.format( "Npc_%d" , v.id )
skynet.server.redis:zadd( redisKey , 0 , robotPartnerId )
skynet.server.personal:SetDetail( robotPartnerId ,"decoWeekDesignTime", 0 )
end
--self:TestNpc()
end
--检测额外的曝光次数
function RankDecoWeek:CheckExtraExposure( activityThemeId )
local redisKey = string.format( redisKeyUrl.GameServerActivityDecoWeekInfoHash )
local returnValue = skynet.server.redis:hsetnx( redisKey , "isCheckExtraExposure" , true )
if 0 == returnValue then
--返回0表示当期已经处理过就不再往下走
return
end
local redisVoteKey = redisKeyUrl.GameServerActivityDecoWeekNeedExpouseCountZSet
local queryHumanData = skynet.server.redis:zrevrange( redisVoteKey , 0 , -1 )
local count = 0
local curTime = skynet.GetTime()
log.info("周赛检测额外的曝光 开始处理" , #queryHumanData , activityThemeId)
--遍历所有玩家
for k, partnerId in pairs( queryHumanData ) do
function Do()
local myDetail = skynet.server.personal:GetDetail( partnerId )
if myDetail and myDetail.rechargeAmount and myDetail.lastWeekGameTime and myDetail.decoWeekOpenShowCount then
local lastWeekGameTime = math.floor( myDetail.lastWeekGameTime / 60 ) --上一周游戏时长(分钟)
local addCount = 0 --额外的曝光次数
--玩家在进入投票阶段前的充值累积金额≥30元时则获得额外20次的曝光机会
if myDetail.rechargeAmount >= 30 then
addCount = addCount + 20
log.info(string.format("装修周赛 检测额外的曝光 玩家 %s 满足充值条件 充值金额 %d 增加次数 %d " , partnerId , myDetail.rechargeAmount or 0, 20))
end
--玩家在投票阶段上一周的累积游戏时长≥400分钟时则获得额外10次曝光机会
if lastWeekGameTime >= 400 then
addCount = addCount + 10
log.info(string.format("装修周赛 检测额外的曝光 玩家 %s 满足在线时长条件 游戏时长 %d 增加次数 %d " , partnerId , lastWeekGameTime or 0 , 10))
end
--玩家在本次投稿阶段累积完成交互≥35次时则获得额外15次曝光机会
if myDetail.decoWeekOpenShowCount >= 35 then
addCount = addCount + 15
log.info(string.format("装修周赛 检测额外的曝光 玩家 %s 满足交互条件 次数 %d 增加次数 %d " , partnerId , myDetail.decoWeekOpenShowCount or 0 , 15))
end
local redisKey = redisKeyUrl.GameServerActivityDecoWeekNeedExpouseCountZSet
skynet.server.redis:zincrby( redisKey , addCount , partnerId )
end
end
local isDo,callData = pcall( Do )
local sendMsg,sendLen = 0,0
if not isDo then
local errorText = "装修周赛 检测额外的曝光次数 结算报错"
if partnerId then
log.warning(errorText , partnerId , callData )
else
log.warning( errorText , callData)
end
end
count = count + 1
if count >= 100 then
--处理100个玩家睡眠1毫秒
log.info("周赛检测额外的曝光 100玩家时间花费" , skynet.GetTime() - curTime)
skynet.sleep(1)
count = 0
curTime = skynet.GetTime()
end
end
log.info("周赛检测额外的曝光 结束处理" , #queryHumanData , activityThemeId)
end
--计算排名
function RankDecoWeek:CalcRank( activityThemeId )
local redisWeekInfoKey = string.format( redisKeyUrl.GameServerActivityDecoWeekInfoHash )
local returnValue = skynet.server.redis:hsetnx( redisWeekInfoKey , "isGiveRankReward" , true )
if 0 == returnValue then
--返回0表示有其它服务器发了奖励就不再往下走
return
end
local curTime = skynet.GetTime()
--这里可以打开重置活动数据的标记
skynet.server.redis:hdel( redisWeekInfoKey , "isResetActivityData" )
------------------------------------------------------------------------第一步 获取所有排行榜数据------------------------------------------------------------------------
--获取玩家分数
local redisVoteKey = string.format( redisKeyUrl.GameServerActivityDecoWeekHumanVoteScoreZSet , activityThemeId )
local queryHumanData = skynet.server.redis:zrevrangebyscore( redisVoteKey , "+inf" , "-inf" , "withscores" )
queryHumanData = redisKeyUrl:CovertOrderTable( queryHumanData )
log.info("周赛结算步骤1 时间花费" , skynet.GetTime() - curTime)
--获取NPC玩家分数
redisVoteKey = redisKeyUrl.GameServerActivityDecoWeekNpcVoteScoreZSet
local queryNpcData = skynet.server.redis:zrevrangebyscore( redisVoteKey , "+inf" , "-inf" , "withscores" )
queryNpcData = redisKeyUrl:CovertOrderTable( queryNpcData )
log.info("周赛结算步骤2 时间花费" , skynet.GetTime() - curTime)
local queryData = {}
local maxDesignCount = 0 --所有参加设计玩家数量
local cfgSValue = skynet.server.gameConfig:GetAllCfg( "SValue")
for k, v in pairs(queryHumanData) do
maxDesignCount = maxDesignCount + 1
v.value = v.value * cfgSValue.weeklyContestHeatValue --玩家分数乘以热度系数
table.insert( queryData , v )
end
log.info("周赛结算步骤3 时间花费" , skynet.GetTime() - curTime)
for k, v in pairs(queryNpcData) do
maxDesignCount = maxDesignCount + 1
v.value = v.value * cfgSValue.weeklyContestHeatValue --玩家分数乘以热度系数
table.insert( queryData , v )
end
log.info("周赛结算步骤4 时间花费" , skynet.GetTime() - curTime)
local rankId = 0 --排名ID
local rankRatio = 0 --排名百分比(保留两位小数)
local allExposureCount = skynet.server.redis:hget( redisWeekInfoKey , "allExposureCount" ) or 0 --所有曝光次数
local aveExposureCount = math.floor( allExposureCount / maxDesignCount ) --平均曝光次数
log.info("周赛结算步骤5 时间花费" , skynet.GetTime() - curTime)
------------------------------------------------------------------------第二步 根据曝光 补偿分数------------------------------------------------------------------------
--获取需要曝光补偿的玩家数据补偿范围分数1~+inf
local redisNeedExpouseCountKey = redisKeyUrl.GameServerActivityDecoWeekNeedExpouseCountZSet
local queryNeedExpouseData = skynet.server.redis:zrangebyscore( redisNeedExpouseCountKey , 1 , "+inf" , "withscores" )
queryNeedExpouseData = redisKeyUrl:CovertOrderTable( queryNeedExpouseData )
local needExpouseCount = #queryNeedExpouseData
log.info(string.format("装修周赛 需要曝光补偿的玩家数量 %d " , needExpouseCount))
if needExpouseCount > 0 then
--获取需要曝光次数
local function GetNeedExpouseCount( partnerId )
if string.find( partnerId , "Npc_") then
return 0
end
for k, v in pairs( queryNeedExpouseData ) do
if partnerId == v.key then
return v.value
end
end
return 0
end
local redisHumanVoteKey = string.format( redisKeyUrl.GameServerActivityDecoWeekHumanVoteScoreZSet , activityThemeId )
for k , v in pairs( queryData ) do
function Do()
local partnerId = v.key
local subsidyCount = GetNeedExpouseCount( partnerId )
if subsidyCount >0 then
--没有达到平均推荐次数的作品则按((“平均推荐次数-当前推荐次数”) / 10 + 0.5 (为了四舍五入))取整的值,在当前热度值基础上补偿对应的数额
--subsidyCount = math.floor((((aveExposureCount - curDetail.decoWeekExposureCount) / 10 ) + 0.5) )
queryData[ k ].value = queryData[ k ].value + ( subsidyCount * cfgSValue.weeklyContestHeatValue )
skynet.server.redis:zincrby( redisHumanVoteKey , subsidyCount , partnerId )
log.info(string.format("装修周赛 玩家 %s 原分数 %d 补偿分数 %d 最终分数 %d", partnerId , (queryData[ k ].value / (cfgSValue.weeklyContestHeatValue or 1)) - subsidyCount , subsidyCount , queryData[ k ].value))
skynet.sleep(1)
end
end
local isDo,callData = pcall( Do )
local sendMsg,sendLen = 0,0
if not isDo then
local errorText = "装修周赛 补偿分数 结算报错"
if v then
log.warning(errorText , v.key , v.value , callData )
else
log.warning( errorText , callData)
end
end
end
end
log.info("周赛结算步骤6 时间花费" , skynet.GetTime() - curTime)
--对数据进行一个降序排列
table.sort(queryData , function (a,b)
return a.value >b.value
end)
log.info("周赛结算步骤7 时间花费" , skynet.GetTime() - curTime)
------------------------------------------------------------------------第三步 计算所有玩家排名信息,并找出最大排名------------------------------------------------------------------
local lastUserScore = -1 --上一个玩家的分数
for k , v in pairs( queryData ) do
function Do()
local partnerId = v.key
local score = v.value
if -1 == lastUserScore or score < lastUserScore then --第一个玩家直接名次+1或者分数大于上一个玩家名次+1
rankId = rankId + 1
end
lastUserScore = score
queryData[ k ].rankId = rankId
end
local isDo,callData = pcall( Do )
local sendMsg,sendLen = 0,0
if not isDo then
local errorText = "装修周赛 计算名次 结算报错"
if v then
log.warning(errorText , v.key , v.value , callData )
else
log.warning( errorText , callData)
end
end
end
log.info("周赛结算步骤8 时间花费" , skynet.GetTime() - curTime)
--保存下最大设计数量平均曝光数量和最大排名ID
skynet.server.redis:hmset( redisWeekInfoKey , "lastActivityId" , activityThemeId ,"maxDesignCount" , maxDesignCount , "aveExposureCount" , aveExposureCount , "maxRankId" , rankId )
log.info(string.format("装修周赛 计算排名 所有设计人数 %d 所有曝光次数 %d 平均曝光次数 %d 最大名次 %d " , maxDesignCount , allExposureCount , aveExposureCount , rankId))
------------------------------------------------------------------------第四步 给玩家发放离线消息------------------------------------------------------------------
local isSaveTopRank = false
for k , v in pairs( queryData ) do
function Do()
local partnerId = v.key
local myRankId = v.rankId
local score = v.value
local rankRatio , rankRange, rankRewardId = self:GetRewardInfo( activityThemeId , myRankId , rankId)
if rankRewardId > 0 then
--保存下玩家的排名
local msgData = {}
msgData.rankRange = rankRange
msgData.rankRewardId = rankRewardId
msgData.activityThemeId = activityThemeId
--获取7天后18点时间戳
local sendMailTime = skynet.server.common:GetAfterSomeDayTime( 7 ,18)
skynet.server.personal:AddOfflineMsg( partnerId , dataType.OfflineMsgType_ActivityDecoRankId , msgData , 45 ,sendMailTime)
skynet.server.personal:SetDetail( partnerId ,
"decoWeekLastThemeId" , activityThemeId ,
"decoWeekIsLastSubmitDesign" , true ,
"decoWeekRankRange" , rankRange ,
"decoWeekRewardId" , rankRewardId ,
"decoWeekRewardStatus" , dataType.DecoWeekRewardStatus_AddOfflineMsg,
"decoWeekLastRankRange" , rankRange ,
"decoWeekLastRewardId" , rankRewardId,
"decoWeekLastRewardCanGet" , true)
if 1 == myRankId and not isSaveTopRank then
isSaveTopRank = true
self:SaveTopDesign( activityThemeId , partnerId , score )
end
log.info(string.format("装修周赛 玩家 %s 当前排名 %d 分数 %d 当前排名因子 %f 当前排名范围最大因子 %f 奖励ID %d", partnerId , myRankId , score , rankRatio , rankRange , rankRewardId))
skynet.sleep(1)
end
end
local isDo,callData = pcall( Do )
local sendMsg,sendLen = 0,0
if not isDo then
local errorText = "装修周赛 发送离线邮件 结算报错"
if v then
log.warning(errorText , v.key , v.value , callData )
else
log.warning( errorText , callData)
end
end
end
log.info("周赛结算步骤9 时间花费" , skynet.GetTime() - curTime)
end
--改变活动状态
function RankDecoWeek:ChangeStatus( activityThemeId , newActivityStatus )
self.curActivityStatus = newActivityStatus
if self.ActivityStatus_Join == newActivityStatus then
--重置活动数据
self:ResetActivityData( activityThemeId )
elseif self.ActivityStatus_Vote == newActivityStatus then
--被投票的玩家是不是增加额外的曝光次数
self:CheckExtraExposure( activityThemeId )
elseif self.ActivityStatus_Balance == newActivityStatus then
--计算排名
self:CalcRank( activityThemeId )
end
end
--获取当前活动信息
function RankDecoWeek:RefreshActivityInfo()
local newActivityStatus = self.ActivityStatus_No
local curTime = skynet.GetTime()
local cfgAllWeeklyContest = skynet.server.gameConfig:GetAllCfg( "WeeklyContest" )
local startTime,endTime = 0 , 0
for k, v in pairs( cfgAllWeeklyContest ) do
local postBeginTime = skynet.server.common:GetTime( v.postBeginTime )
local postEndTime = skynet.server.common:GetTime( v.postEndTime )
local voteBeginTime = skynet.server.common:GetTime( v.voteBeginTime )
local voteEndTime = skynet.server.common:GetTime( v.voteEndTime )
local settleBeginTime = skynet.server.common:GetTime( v.settleBeginTime )
local settleEndTime = skynet.server.common:GetTime( v.settleEndTime )
--根据配置时间获取当前阶段
if curTime >= postBeginTime and curTime <= postEndTime then
newActivityStatus = self.ActivityStatus_Join
startTime = postBeginTime
endTime = postEndTime
elseif curTime >= voteBeginTime and curTime <= voteEndTime then
newActivityStatus = self.ActivityStatus_Vote
startTime = voteBeginTime
endTime = voteEndTime
elseif curTime >= settleBeginTime and curTime <= settleEndTime then
newActivityStatus = self.ActivityStatus_Balance
startTime = settleBeginTime
endTime = settleEndTime
end
--修改最新的活动ID和状态切状态时同步下redis上的数据
if self.ActivityStatus_No ~= newActivityStatus then
--newActivityStatus = 2
local redisKey = string.format( redisKeyUrl.GameServerActivityDecoWeekInfoHash )
skynet.server.redis:hmset( redisKey , "activityThemeId" , v.id , "activityStatus" , newActivityStatus , "stageStartTime" , startTime , "stageEndTime" , endTime )
if self.curActivityStatus ~= newActivityStatus then
log.info(string.format("装修周赛 设置最新活动详情 期数 %d 当前状态 %d 最新状态 %d 开始时间 %s 结束时间 %s" , v.id , self.curActivityStatus , newActivityStatus ,
skynet.server.common:GetStrTime(startTime) , skynet.server.common:GetStrTime(endTime)))
self:ChangeStatus( v.id , newActivityStatus )
end
return v.id , newActivityStatus , startTime , endTime
end
end
self.curActivityStatus = self.ActivityStatus_No
return 0 , 0 , 0 , 0
end
--保存分数最高的玩家
function RankDecoWeek:SaveTopDesign( activityThemeId , partnerId , score )
--将第一名的设计只在下来
local redisKey = redisKeyUrl.GameServerActivityDecoWeekTopDesignList
local curDetail = skynet.server.personal:GetDetail( partnerId ) --获取个人详细信息
if curDetail then
local topDesign = {}
topDesign.themeId = activityThemeId
topDesign.partnerId = partnerId
topDesign.nickName = curDetail.nickName
topDesign.score = score --* cfgSValue.weeklyContestHeatValue
if skynet.server.gameConfig:IsOnline() then
topDesign.designUrl = curDetail.decoWeekDesignOssUrl
else
topDesign.designUrl = curDetail.decoWeekDesignUrl
end
topDesign.designTime = curDetail.decoWeekDesignTime
skynet.server.redis:lpush( redisKey , json:encode(topDesign))
else
log.info(string.format("装修周赛 NPC %s 为第一名 没有个人信息 不保存到热门设计中", partnerId ))
end
end
--获取奖励信息
function RankDecoWeek:GetRewardInfo( activityThemeId , myRankId , maxRankId )
local cfgOneWeekly = skynet.server.gameConfig:GetCurCfg( "WeeklyContest" , activityThemeId )
local rankRange = cfgOneWeekly.rankRange --排名范围
local rankReward = cfgOneWeekly.rankReward --排名范围奖励
local rankRatio = tonumber(string.format("%.2f", myRankId / maxRankId ))
for i = 1, #rankRange, 1 do
if rankRatio <= rankRange[i] then
return rankRatio , rankRange[i] , rankReward[i]
end
end
return 0.0 , 0.0 , 0
end
--测试NPC
function RankDecoWeek:TestNpc()
--初始化NPC的分数和设计时间
local redisKey = redisKeyUrl.GameServerActivityDecoWeekNpcVoteScoreZSet
local cfgAllNPCName = skynet.server.gameConfig:GetAllCfg( "NPCName")
local startPos = 0
for k, v in pairs( cfgAllNPCName ) do
local robotPartnerId = string.format( "Npc_%d" ,startPos + v.id )
skynet.server.redis:zadd( redisKey , math.random(10, 20) , robotPartnerId )
skynet.server.personal:SetDetail( robotPartnerId ,"decoWeekDesignTime", 0 )
end
startPos = 200
for k, v in pairs( cfgAllNPCName ) do
local robotPartnerId = string.format( "Npc_%d" ,startPos + v.id )
--skynet.server.redis:zadd( redisKey , 0 , robotPartnerId ) 测试要删
skynet.server.redis:zadd( redisKey , math.random(20, 40) , robotPartnerId )
skynet.server.personal:SetDetail( robotPartnerId ,"decoWeekDesignTime", 0 )
end
startPos = 400
for k, v in pairs( cfgAllNPCName ) do
local robotPartnerId = string.format( "Npc_%d" , startPos +v.id )
--skynet.server.redis:zadd( redisKey , 0 , robotPartnerId ) 测试要删
skynet.server.redis:zadd( redisKey , math.random(40, 80) , robotPartnerId )
skynet.server.personal:SetDetail( robotPartnerId ,"decoWeekDesignTime", 0 )
end
end
skynet.server.rankDecoWeek = RankDecoWeek
return RankDecoWeek