HomeServer/Server/AllServer/GameServer/Announcement.lua

366 lines
17 KiB
Lua
Raw 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 redisKeyUrl = require "RedisKeyUrl"
local json = require "json"
local common = require "Common"
local Announcement = oo.class()
Announcement.AnnouncementOpType_1 = 1 --获取公告列表
Announcement.AnnouncementOpStatus_Suc = 1 --操作状态成功
Announcement.AnnouncementOpStatus_NoAnnouncementID = 2 --不存在该公告ID
Announcement.MaxSendCount = 100 --一次发送最大数量
function Announcement:Init()
self.announcementList = {} --游戏服缓存公告
self.announcementListCache = {} --已推送的公告
self.announcementUpdateTime =0 --公告更新时间
self.announcementDisableTime =0 --失效公告移除时间 服务于客户端申请公告列表时做比对,如果本地有缓存且公告更新时间一致再比对失效公告移除时间和客户端更新时间,如果不一致则将新的公告列表返回
self:ServerAnnouncementDataSync() --服务器重启丢失缓存所以在初始化时读取redis中的数据
end
--登陆初始化数据
function Announcement:LoginInitData( player )
self:CheckNewAnnouncement( player )
end
--每5秒调一次
function Announcement:On5SecTimer()
self:BatchSendTips()
end
--从后台刷新公告列表
function Announcement:RefreshAnnouncementList()
--向后端请求
local param = { backstageId = 1 }
local web = skynet.server.gameConfig.WebConfig.host .. ":" .. skynet.server.gameConfig.WebConfig.port
local url = skynet.server.common.getAnnouncementUrl
local status, body = skynet.server.httpClient:PostJson(web, url, json:encode(param), "http")
if 200 == status then
-- newbody 即后台的 ResultModel
local newbody = json:decode(body)
if 200 == newbody.code then
--先设置所有旧公告状态失效 (可以考虑给游戏服缓存list加锁防止更新状态时其他地方操作)
--self.announcementLock = true
local updateTag = false
if next(Announcement.announcementList)~=nil then
for k ,v in pairs(Announcement.announcementList) do
v.canGet = false
end
end
--添加新的公告
for k, v in pairs(newbody.data) do
if not Announcement.announcementList[v.id] then
local announcementId = v.id
Announcement.announcementList[announcementId] = {}
local curAnnounce = Announcement.announcementList[announcementId]
curAnnounce.announcementId = announcementId
curAnnounce.status = dataType.AnnouncementStatus_NoGet
curAnnounce.announcementRemarks = v.remarks
curAnnounce.announcementWeight = v.weight
curAnnounce.title = v.announcementTitle
curAnnounce.content = v.announcementText
curAnnounce.receiveTime = skynet.GetTime()
curAnnounce.canGet = true
curAnnounce.isSendAllOnlineUser = false --是否发送所有在线玩家
--只显示一张图片
if v.img then
--处理下微小图片访问问题
local data = skynet.server.common:Split(v.img[1],":")
if data[2] == "//47.96.98.191" then
data = skynet.server.common:Split(v.img[1],"/")
curAnnounce.image = "https://web.myftime2024.skydreams.cn/" .. data[#data - 1] .. "/" .. data[#data]
else
curAnnounce.image = v.img[1]
end
end
--只有一个外链
if v.href then
curAnnounce.href = v.href[1]
end
curAnnounce.announcementTypeName = v.announcementType --公告类型名称
curAnnounce.gameId = v.gameId --游戏cgi
curAnnounce.useChannel = v.useChannel --使用渠道
curAnnounce.minVersion = tonumber(v.minVersion) --最小版本号
curAnnounce.operatingSystem = v.operatingSystem --操作系统
Announcement.announcementUpdateTime =skynet.GetTime() --刷新公告更新时间
updateTag = true
else --仍存在的公告状态设置为未失效
if v.img then
Announcement.announcementList[v.id].image = v.img[1]
end
Announcement.announcementList[v.id].canGet = true
end
end
--判断公告缓存是否还有效,若无效则移出游戏服缓存中
if next(Announcement.announcementList)~=nil then
for k ,v in pairs(Announcement.announcementList) do
if not v.canGet then
self.announcementList[k] =nil
--table.remove(self.announcementList,k)
if self.announcementListCache[k] then
self.announcementListCache[k] =nil
end
Announcement.announcementDisableTime =skynet.GetTime() --刷新失效公告时间
updateTag = true
end
end
end
--self.announcementLock = false --释放锁
if updateTag then
self:SaveServerAnnouncementData()
end
elseif 802 == newbody.code then
--s2cData.code = errorInfo.ErrorCode.AlreadyGet
--没有公告情况下移除缓存池的所有公告
if next(Announcement.announcementList)~=nil then
if next(Announcement.announcementList)~=nil then
Announcement.announcementList = {}
self.announcementListCache = {}
Announcement.announcementDisableTime =skynet.GetTime() --刷新失效公告时间
self:SaveServerAnnouncementData()
end
end
end
else
log.info("公告刷新 获取数据失败 status ", status or 0)
end
end
--拉取最新公告
function Announcement:GetLatestAnnouncement(player, c2sData, s2cData)
--重置外部红点
skynet.server.msgTips:Reset(player , 101)
c2sData.data = assert(pb.decode("C2SAnnouncementNew", c2sData.data))
local data = {}
data.announcementInfo = {}
--获取客户端上一次申请公告请求的时间
local localAnnouncementUpdateTime = c2sData.data.localAnnouncementUpdateTime
--该时间>公告更新时间&公告失效时间则返回 1 通知客户端使用本地缓存 否则返回 2 并且发送当前缓存列表的公告
if localAnnouncementUpdateTime and localAnnouncementUpdateTime > Announcement.announcementUpdateTime and localAnnouncementUpdateTime > Announcement.announcementDisableTime and localAnnouncementUpdateTime < skynet.GetTime() then
data.backType = 1
s2cData.cmd = pb.enum("MsgType", "CMD_S2C_AnnouncementNew")
s2cData.data = assert(pb.encode("S2CAnnouncementNew", data))
else
data.backType = 2
data.announcementUpdateTime = skynet.GetTime()
player.gameData.announcementUpdateTime =Announcement.announcementUpdateTime
for k , v in pairs(Announcement.announcementList) do
if player.basicInfo.gameCgi ~= v.gameId then
goto continue
end
--玩家的渠道是否符合
if player.basicInfo.channel ~= v.useChannel and "all" ~= v.useChannel then
goto continue
end
--玩家的APP版本是否符合
if player.basicInfo.appVersion < v.minVersion then
goto continue
end
--玩家的系统是否符合
if player.basicInfo.system ~= v.operatingSystem and "all" ~= v.operatingSystem and "双端" ~= v.operatingSystem then
goto continue
end
local info = skynet.server.common:DeepCopy(v)
--处理单引号的问题
info.content = string.gsub(v.content,"&#39;","'")
info.content = string.gsub(v.content,"&nbsp;"," ")
table.insert(data.announcementInfo,info)
::continue::
end
s2cData.cmd = pb.enum("MsgType", "CMD_S2C_AnnouncementNew")
s2cData.data = assert(pb.encode("S2CAnnouncementNew", data))
end
end
--添加公告提醒
function Announcement:AddAnnouncementNotice(player,announcementInfo)
--玩家当前游戏的包是否匹配当前公告的cgi
if player.basicInfo.gameCgi ~= announcementInfo.gameId then
return false
end
--玩家的渠道是否符合
if player.basicInfo.channel ~= announcementInfo.useChannel and "all" ~= announcementInfo.useChannel then
return false
end
--玩家的APP版本是否符合
if player.basicInfo.appVersion < announcementInfo.minVersion then
return false
end
--玩家的系统是否符合
if player.basicInfo.system ~= announcementInfo.operatingSystem and "all" ~= announcementInfo.operatingSystem and "双端" ~= announcementInfo.operatingSystem then
return false
end
--向玩家推送红点
skynet.server.msgTips:Add(player , 101)
return true
end
--新的登陆初始化函数,更新时记得解开注释,然后将旧函数注释
function Announcement:LoginInitData(player)
--如果玩家登陆时不存在公告更新时间字段或者该字段小于当前游戏服最新的公告更新时间则推送红点提示
if not player.gameData.announcementUpdateTime or player.gameData.announcementUpdateTime < Announcement.announcementUpdateTime then
for announceId, v1 in pairs( self.announcementList ) do
self:AddAnnouncementNotice(player,v1)
end
--刷新玩家获取公告时间
player.gameData.announcementUpdateTime =Announcement.announcementUpdateTime
end
end
--是否存在公告(待废弃)
function Announcement:IsExist(player, announcementId)
for k, v in pairs(player.gameData.announcement.historyAnnouncement) do
if announcementId == v then
return true
end
end
return false
end
--批量推送公告更新提示
function Announcement:BatchSendTips()
local t1 = skynet.GetTime()
local playerList = skynet.server.playerCenter:GetPlayerList()
local sendCount = 0 --此次推送公告数量
local isSendAnnounce = true --是否继续推送公告
for announceId, v1 in pairs( self.announcementList ) do
--在已推送完毕的公告池中进行检索,如果已经被推送过则跳过后续的推送逻辑节省性能(不会存在推送过程中新登陆的玩家收不到新公告推送的问题,登陆的消息初始化中会调用专门的函数查询)
if self.announcementListCache[announceId] then
goto continue
end
for userId, value in pairs(playerList) do
if value.status == skynet.server.playerCenter.Status_Playing and value.player.gameData.announcementUpdateTime and value.player.gameData.announcementUpdateTime < Announcement.announcementUpdateTime then --玩家获取过公告,但可能存在新的公告
--调用推送公告提醒函数
if self:AddAnnouncementNotice(value.player,v1) then
--刷新玩家获取公告时间
value.player.gameData.announcementUpdateTime =Announcement.announcementUpdateTime
sendCount = sendCount + 1
if sendCount >= self.MaxSendCount then
isSendAnnounce = false
break
end
end
elseif value.status == skynet.server.playerCenter.Status_Playing and not value.player.gameData.announcementUpdateTime then --玩家没有获取过公告(公告系统逻辑迭代,主要作用于不存在该属性的旧版本玩家)
if self:AddAnnouncementNotice(value.player,v1) then
value.player.gameData.announcementUpdateTime =Announcement.announcementUpdateTime
sendCount = sendCount + 1
if sendCount >= self.MaxSendCount then
isSendAnnounce = false
break
end
end
end
end
if sendCount == 0 then
self.announcementListCache[announceId] = true
self:SaveServerAnnouncementData()
log.info(string.format("公告 全服公告 %s 在线已发送结束 ", announceId ))
else
log.info(string.format("公告 全服公告 %s 发送 发送数量 %d 时间花费 %d ", announceId , sendCount, skynet.GetTime() - t1))
end
if not isSendAnnounce then
break
end
::continue::
end
end
--保存游戏服公告的缓存到Redis做备份
function Announcement:SaveServerAnnouncementData()
local redisKey = redisKeyUrl.GameServerAnnoucementCache
local data = {}
data.announcementList ={}
for k,v in pairs(self.announcementList) do
table.insert(data.announcementList,v)
end
data.announcementListCache = {}
for k,v in pairs(self.announcementListCache) do
table.insert(data.announcementListCache,k)
end
data.announcementUpdateTime=self.announcementUpdateTime
data.announcementDisableTime=self.announcementDisableTime
skynet.server.redis:set( redisKey , json:encode(data) )
end
--同步Redis中的公告缓存 一般在服务器重启时调用
function Announcement:ServerAnnouncementDataSync()
local redisKey = redisKeyUrl.GameServerAnnoucementCache
if not skynet.server.redis:exists(redisKey) then
return
end
local cache = skynet.server.redis:get(redisKey)
cache = json:decode(cache)
if not cache.announcementUpdateTime and cache.announcementUpdateTime > self.announcementUpdateTime then
for k,v in pairs(cache.announcementList) do
self.announcementList[v.announcementId] = v
end
for k,v in pairs(cache.announcementListCache) do
self.announcementListCache[v] = true
end
self.announcementUpdateTime = cache.announcementUpdateTime
self.announcementDisableTime = cache.announcementDisableTime
end
end
--调用服务器的添加公告方法 标题 内容 类型
function Announcement:AddAnnouncementByBS(title , content , typeId)
--取消方法
if true then
return
end
--platform 操作系统 默认双端
local platform = "双端"
--useChannel 使用渠道 默认所有渠道
local channel = "all"
local playerList = skynet.server.playerCenter:GetPlayerList()
--gameCgi 游戏id
local gameCgi = ""
--version 最小版本号
local version = 0
for k ,v in pairs(playerList) do
--从在线玩家中获取相关数据
if skynet.server.playerCenter.Status_Playing == v.status and v.player.basicInfo.gameCgi and v.player.basicInfo.appVersion then
gameCgi = v.player.basicInfo.gameCgi
version = v.player.basicInfo.appVersion
break
end
end
if gameCgi == "" and version == 0 then
return true
end
--startTime 生效时间
local startTime = skynet.server.common:GetAfterSomeDay(0)
--endTime 失效时间 公告存在一天
local endTime = startTime + 24 * 60 * 60
--向后端请求
startTime = skynet.server.common:GetStrTime(startTime)
endTime = skynet.server.common:GetStrTime(endTime)
local param = {gameId = gameCgi , useChannel = channel , minVersion = tostring(version) ,
effectiveTime = startTime , failureTime = endTime ,
announcementTitle = title , announcementText = content , operatingSystem = platform}
local web = skynet.server.gameConfig.WebConfig.host .. ":" .. skynet.server.gameConfig.WebConfig.port
local url = skynet.server.common.addAnnouncementUrl
local status, body = skynet.server.httpClient:PostJson(web, url, json:encode(param), "http")
if 200 == status then
local newbody = json:decode(body)
if 200 == newbody.code then
--添加了公告过后 再获取一遍
self:RefreshAnnouncementList()
end
end
end
--相当于单例
skynet.server.announcement = Announcement
return Announcement