game_sync/worldsrv/lotterymgr.go

767 lines
18 KiB
Go

package main
import (
"fmt"
"math/rand"
"sort"
"strconv"
"time"
"mongo.games.com/goserver/core/basic"
"mongo.games.com/goserver/core/logger"
"mongo.games.com/goserver/core/module"
"mongo.games.com/goserver/core/task"
"mongo.games.com/game/common"
"mongo.games.com/game/model"
"mongo.games.com/game/mq"
"mongo.games.com/game/protocol/webapi"
"mongo.games.com/game/protocol/welfare"
)
const (
LotteryRoomCard = 10 // 几张房卡获得一个抽奖码
)
var LotteryMgrInst = &LotteryMgr{
Data: make(map[string]map[int64]*LotteryData),
PlatformConfig: make(map[string]*LotteryConfig),
}
func init() {
module.RegisteModule(LotteryMgrInst, time.Minute, 0)
common.RegisterClockFunc(&common.ClockFunc{
OnDayTimerFunc: func() {
for _, v := range LotteryMgrInst.PlatformConfig {
if v == nil {
continue
}
if v.IsCycle {
logger.Logger.Tracef("LotteryMgrInst OnDayChange Reset")
// 每天重置抽奖数据
LotteryMgrInst.Reset()
// 重置玩家抽奖数据
for _, v := range PlayerInfoMgrSingle.Players {
v.Lottery = make(map[int64]*model.Lottery)
}
}
}
},
})
}
// LotteryData 抽奖数据
type LotteryData struct {
*model.LotteryData
isDone bool // 抽奖中
}
// Reset 重置抽奖数据
func (l *LotteryData) Reset() {
if l == nil {
return
}
cfg := PlatformMgrSingleton.GetLotteryConfig(l.Platform, l.CId)
if cfg == nil {
return
}
l.StartTs = common.IntToTime(int(cfg.GetStartHMS())).Unix()
l.EndTs = common.IntToTime(int(cfg.GetEndHMS())).Unix()
l.WinTs = common.IntToTime(int(cfg.GetWinHMS())).Unix()
l.Price = cfg.GetPrice()
l.RobotCodeNum = cfg.GetRobotCode()
l.TotalCode = cfg.GetTotalCode()
l.Codes = []int{}
if l.TotalCode > 0 {
l.Codes = rand.Perm(int(l.TotalCode))
}
l.Reward = nil
for _, v := range cfg.GetReward() {
l.Reward = append(l.Reward, &model.ItemInfo{
ItemId: v.GetItemId(),
ItemNum: v.GetItemNum(),
})
}
long := len(fmt.Sprintf("%d", cfg.GetTotalCode()-1))
if long < 1 {
long = 1
}
l.Format = "%0" + fmt.Sprintf("%d", long) + "d"
l.ImageURL = cfg.GetImageURI()
l.CustomAward = 0
l.Code = 0
l.PlayerIndex = 0
l.RobotIndex = 0
l.SnId = 0
l.Name = ""
l.RoleId = 0
l.WinCostCard = 0
l.WinCode = ""
l.IsRobot = false
l.IsSend = false
l.CostCard = 0
l.RobotCodeCount = 0
l.PlayerNum = 0
logger.Logger.Tracef("LotteryData Reset: %v", l.CId)
}
// GetCode 获取抽奖码
func (l *LotteryData) GetCode() (string, bool) {
if l == nil {
return "", false
}
if l.Code < int(l.TotalCode) {
var ret string
if len(l.Codes) == 0 {
ret = fmt.Sprintf("%d", l.Code)
} else {
ret = fmt.Sprintf("%d", l.Codes[l.Code])
}
l.Code++
return ret, true
}
return "", false
}
func (l *LotteryData) GetPlayerIndex() int {
l.PlayerIndex++
return l.PlayerIndex
}
func (l *LotteryData) GetRobotIndex() int {
l.RobotIndex++
return l.RobotIndex
}
// GetRemainCode 获取剩余抽奖码数量
func (l *LotteryData) GetRemainCode() int {
if l == nil {
return 0
}
return int(l.TotalCode) - l.Code
}
// 发奖
func (l *LotteryData) sendAward(must bool) {
if l.isDone || !must {
return
}
now := time.Now()
if l.WinTs <= 0 || l.WinTs >= now.Unix() || l.IsSend || l.WinCode == "" || l.SnId == 0 || now.Unix()-l.WinTs > 5*60 {
return
}
l.IsSend = true
mq.Write(&model.LotteryLog{Platform: l.Platform}, mq.RankLotteryLog)
var lotteryAward []*welfare.PropInfo
for _, v := range l.Reward {
lotteryAward = append(lotteryAward, &welfare.PropInfo{
ItemId: v.ItemId,
ItemNum: v.ItemNum,
})
}
pack := &welfare.NotifyLotteryAward{
Info: &welfare.LotteryInfo{
Id: l.CId,
StartTs: l.StartTs,
Index: int32(l.Num),
Award: lotteryAward,
SnId: l.SnId,
Name: l.Name,
RoleId: l.RoleId,
Price: l.Price,
WinCode: l.WinCode,
},
}
// 广播中奖结果
PlayerMgrSington.BroadcastMessageToPlatform(l.Platform, int(welfare.SPacketID_PACKET_NotifyLotteryAward), pack)
logger.Logger.Tracef("NotifyLotteryAward: %v", pack)
if l.IsRobot {
return
}
AddMailLottery(l.Platform, l.SnId, l.Reward)
}
func (l *LotteryData) sendRobotCode(a, b int) {
// 随机给机器人发放抽奖码
now := time.Now()
if l.StartTs <= now.Unix() && l.EndTs > now.Unix() && l.RobotCodeCount < int(l.RobotCodeNum) && l.GetRemainCode() > 0 {
n := common.RandInt(a, b)
for i := 0; i < n; i++ {
code, b := l.GetCode()
if b {
l.RobotCodeCount++
// 开奖码记录
mq.Write(&model.LotteryCode{
Platform: l.Platform,
SnId: 0,
CId: l.CId,
StartTs: l.StartTs,
Code: code,
Index: l.GetRobotIndex(),
})
if l.RobotCodeCount >= int(l.RobotCodeNum) || l.GetRemainCode() <= 0 {
break
}
}
}
}
}
// Done 抽奖
func (l *LotteryData) Done() {
if l.isDone {
return
}
l.isDone = true
logger.Logger.Tracef("LotteryData Done1: cid:%v wincode:%v snid:%v", l.CId, l.WinCode, l.SnId)
now := time.Now()
if l.EndTs <= 0 || l.EndTs >= now.Unix() || l.SnId > 0 || now.Unix()-l.EndTs > 5*60 {
l.isDone = false
return
}
sTs := common.GetDayStartTs(now.Unix())
eTs := sTs + int64(time.Hour.Seconds()*24)
if l.StartTs < sTs || l.StartTs >= eTs {
l.isDone = false
return
}
lotteryLog := &model.LotteryLog{} // 中奖记录
var err error
var awardPlayer *Player // 中奖玩家
var joinNum int // 参与人数
var code *model.LotteryCode // 开奖码
var costCard int64 // 消耗房卡
var isMust bool // 是否必中
var index int // 开奖码序号
var tp int // 开奖类型
var roleId int32 = common.DefaultRoleId
// 先随机一个机器人
for _, v := range PlayerMgrSington.snidMap {
if v == nil {
continue
}
if !v.IsRob {
continue
}
awardPlayer = v
break
}
if awardPlayer == nil {
awardPlayer = &Player{
PlayerData: &model.PlayerData{},
}
}
// 是否必中
for _, v := range PlatformMgrSingleton.GetConfig(l.Platform).LotteryUser {
if v.GetOn() != common.On {
continue
}
t, _ := time.Parse(time.DateTime, v.GetTime())
if common.TsInSameDay(t.Unix(), l.StartTs) && int(v.GetNum()) == LotteryMgrInst.GetIndex(l.Platform, l.CId) {
// 必中
isMust = true
tp = 1
index = common.RandInt(1, l.RobotIndex)
awardPlayer.SnId = v.GetSnId()
awardPlayer.IsRob = false
logger.Logger.Tracef("LotteryData 抽奖 isMust:%v tp:%v index:%v RobotIndex:%v awardPlayer:%v", isMust, tp, index, l.RobotIndex, awardPlayer.SnId)
break
}
}
if !isMust {
value := float64(l.CostCard*10-l.CustomAward) / float64(l.Price)
switch {
case value <= 1.2:
// 机器人开奖
tp = 1
index = common.RandInt(1, l.RobotIndex)
case value <= 5:
// 机器人加玩家开奖
tp = 2
index = common.RandInt(1, l.Code)
default:
// 玩家开奖
tp = 3
index = common.RandInt(1, l.PlayerIndex)
}
logger.Logger.Tracef("LotteryData 抽奖 value:%v tp:%v index:%v RobotIndex:%v Code:%v PlayerIndex:%v Cid:%v",
value, tp, index, l.RobotIndex, l.Code, l.PlayerIndex, l.CId)
}
task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
// 查询参与人数
joinNum, err = model.GetLotteryCodeJoinNum(l.Platform, l.CId, l.StartTs)
if err != nil {
logger.Logger.Errorf("GetLotteryCodeJoinNum error:%v", err)
return err
}
// 查询开奖码
code, err = model.GetLotteryCodeRandom(l.Platform, l.CId, l.StartTs, tp, index)
if err != nil {
logger.Logger.Errorf("GetLotteryCodeRandom error:%v", err)
return err
}
if code != nil && isMust {
code.SnId = awardPlayer.SnId
}
if code == nil && isMust {
code = &model.LotteryCode{
Platform: l.Platform,
SnId: awardPlayer.SnId,
CId: l.CId,
StartTs: l.StartTs,
Code: fmt.Sprintf("%d", l.TotalCode),
}
}
// 查询玩家消耗
if code != nil && code.SnId > 0 {
list, err := model.GetLottery(l.Platform, code.SnId, l.CId, l.StartTs)
if err != nil {
logger.Logger.Errorf("GetLottery error:%v", err)
return err
}
for _, v := range list {
if v.CId == l.CId && v.StartTs == l.StartTs {
costCard = v.CostCard
break
}
}
p := PlayerMgrSington.GetPlayerBySnId(code.SnId)
if p == nil {
playerData := model.GetPlayerBaseInfo(l.Platform, code.SnId)
if playerData != nil {
awardPlayer = &Player{
PlayerData: &model.PlayerData{
SnId: playerData.SnId,
Name: playerData.Name,
Roles: playerData.Roles,
},
}
}
}
}
return nil
}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
defer func() {
l.isDone = false
}()
if code == nil {
return
}
if code.SnId > 0 {
p := PlayerMgrSington.GetPlayerBySnId(code.SnId)
if p != nil {
awardPlayer = p
if info := PlayerInfoMgrSingle.Players[p.SnId]; info != nil {
c := info.Lottery[l.CId]
if c != nil && c.StartTs == l.StartTs {
costCard = c.CostCard
}
}
}
}
l.PlayerNum = int32(joinNum)
// 记录中奖结果
if awardPlayer != nil {
if awardPlayer.Roles != nil {
roleId = awardPlayer.Roles.ModId
}
l.Num = LotteryMgrInst.GetIndex(l.Platform, l.CId)
l.SnId = awardPlayer.SnId
l.Name = awardPlayer.Name
l.WinCostCard = costCard
l.WinCode = code.Code
l.IsRobot = awardPlayer.IsRobot()
l.RoleId = roleId
var lotteryAward []*model.LotteryAward
for _, v := range l.Reward {
lotteryAward = append(lotteryAward, &model.LotteryAward{
Id: int64(v.ItemId),
N: v.ItemNum,
})
}
lotteryLog = &model.LotteryLog{
Platform: l.Platform,
CId: l.CId,
CTime: time.Unix(l.StartTs, 0),
Num: int32(l.Num),
SnId: awardPlayer.SnId,
Name: awardPlayer.Name,
RoleId: roleId,
PlayerNum: int64(joinNum),
Code: l.WinCode,
CostCard: l.CostCard,
IsRobot: awardPlayer.IsRobot(),
Award: lotteryAward,
Price: l.Price,
IsMust: isMust,
ImageURL: l.ImageURL,
Ts: l.WinTs,
}
mq.Write(lotteryLog)
logger.Logger.Tracef("LotteryData 抽奖中奖记录: %+v %p", *lotteryLog, l)
logger.Logger.Tracef("LotteryData Done2: cid:%v wincode:%v snid:%v", l.CId, l.WinCode, l.SnId)
// 开始发奖
l.sendAward(true)
}
})).StartByExecutor(fmt.Sprintf("LotterySendAward_%s_%d", l.Platform, l.CId))
}
type LotteryConfig struct {
IsCycle bool
}
// LotteryMgr 抽奖管理
type LotteryMgr struct {
Data map[string]map[int64]*LotteryData // 平台:抽奖配置id:抽奖数据
PlatformConfig map[string]*LotteryConfig
}
func (l *LotteryMgr) ModuleName() string {
return "LotteryMgr"
}
func (l *LotteryMgr) Init() {
// 加载抽奖活动数据
for _, v := range PlatformMgrSingleton.GetPlatforms() {
if v != nil {
data, err := model.GetLotteryData(v.IdStr)
if err != nil {
panic(fmt.Sprintf("GetLotteryData error:%v", err))
}
for _, d := range data {
if PlatformMgrSingleton.GetLotteryConfig(v.IdStr, d.CId) == nil {
model.DelLotteryData(v.IdStr, d.CId)
continue
}
ld := l.GetData(v.IdStr, d.CId)
ld.LotteryData = d
ld.Platform = v.IdStr
if l.PlatformConfig[v.IdStr] != nil {
if !common.TsInSameDay(ld.StartTs, time.Now().Unix()) && l.PlatformConfig[v.IdStr].IsCycle {
ld.Reset()
}
}
}
}
}
}
func (l *LotteryMgr) Update() {
for i := range l.Data {
for k := range l.Data[i] {
d := l.Data[i][k]
if d == nil {
continue
}
lc := PlatformMgrSingleton.GetLotteryConfig(d.Platform, d.CId)
if lc == nil || lc.GetOn() != common.On {
continue
}
// 随机给机器人发放抽奖码
d.sendRobotCode(1, 5)
// 活动结束,开始抽奖
logger.Logger.Tracef("Done1 cid:%v wincode:%v %p", d.CId, d.WinCode, d)
d.Done()
logger.Logger.Tracef("Done2 cid:%v wincode:%v %p", d.CId, d.WinCode, d)
// 开始发奖
d.sendAward(false)
}
}
}
func (l *LotteryMgr) Shutdown() {
for k, v := range l.Data {
var arr []*model.LotteryData
for _, d := range v {
if d.LotteryData == nil {
continue
}
arr = append(arr, d.LotteryData)
}
model.UpsertLotteryData(k, arr)
}
module.UnregisteModule(l)
}
func (l *LotteryMgr) Reset() {
// 重置抽奖数据
for _, v := range LotteryMgrInst.Data {
for _, d := range v {
if d == nil {
continue
}
d.Reset()
}
}
}
func (l *LotteryMgr) UpdateConfig(conf *webapi.LotteryConfig) {
now := time.Now()
// 删除不存在的活动
for k, v := range l.Data {
var id []int64
for _, d := range v {
if d == nil {
continue
}
has := false
for _, vv := range conf.GetList() {
if vv.GetId() == d.CId {
has = true
break
}
}
if !has {
id = append(id, d.CId)
}
}
for _, i := range id {
delete(l.Data[k], i)
}
}
// 更新活动配置,已结束,开始中的不能修改
for _, v := range conf.GetList() {
if v.GetTotalCode() <= 0 {
v.TotalCode = 1
}
data := l.GetData(conf.GetPlatform(), v.GetId())
if data.EndTs > 0 && (data.EndTs < now.Unix() || data.StartTs <= now.Unix()) {
continue
} else {
data.Reset()
}
}
l.GetConfig(conf.GetPlatform()).IsCycle = conf.GetCycle() == common.On
}
func (l *LotteryMgr) GetConfig(plt string) *LotteryConfig {
_, ok := l.PlatformConfig[plt]
if !ok {
l.PlatformConfig[plt] = &LotteryConfig{}
}
return l.PlatformConfig[plt]
}
func (l *LotteryMgr) GetData(plt string, cid int64) *LotteryData {
if l.Data[plt] == nil {
l.Data[plt] = make(map[int64]*LotteryData)
}
if l.Data[plt][cid] == nil {
l.Data[plt][cid] = &LotteryData{
LotteryData: &model.LotteryData{
Platform: plt,
CId: cid,
},
}
}
return l.Data[plt][cid]
}
func (l *LotteryMgr) GetIndex(plt string, cid int64) int {
var arr []*LotteryData
for _, d := range l.Data[plt] {
lc := PlatformMgrSingleton.GetLotteryConfig(d.Platform, d.CId)
if lc == nil || lc.GetOn() != common.On {
continue
}
arr = append(arr, d)
}
sort.Slice(arr, func(i, j int) bool {
if arr[i].StartTs == arr[j].StartTs {
return arr[i].EndTs < arr[j].EndTs
}
return arr[i].StartTs < arr[j].StartTs
})
for k, v := range arr {
if v.CId == cid {
return k + 1
}
}
return 0
}
// GetList 获取抽奖列表
func (l *LotteryMgr) GetList(plt string) []*welfare.LotteryInfo {
now := time.Now()
ret := make([]*welfare.LotteryInfo, 0)
for _, d := range l.Data[plt] {
lc := PlatformMgrSingleton.GetLotteryConfig(d.Platform, d.CId)
if lc == nil || lc.GetOn() != common.On {
continue
}
// 是否活动已经结束
p := l.GetConfig(d.Platform)
if !p.IsCycle && !common.TsInSameDay(d.StartTs, now.Unix()) {
continue
}
var award []*welfare.PropInfo
for _, a := range d.Reward {
award = append(award, &welfare.PropInfo{
ItemId: a.ItemId,
ItemNum: a.ItemNum,
})
}
state := 1
switch {
case d.WinTs <= now.Unix():
state = common.LotteryStateOver // 已结束
case d.StartTs > now.Unix():
state = common.LotteryStateNoStart // 未开始
default:
state = common.LotteryStateRun // 进行中
}
info := &welfare.LotteryInfo{
Id: d.CId,
StartTs: d.StartTs,
EndTs: d.EndTs,
WinTs: d.WinTs,
RemainCode: int64(d.GetRemainCode()),
TotalCode: d.TotalCode,
Award: award,
State: int32(state),
WinCode: d.WinCode,
SnId: d.SnId,
Name: d.Name,
RoleId: d.RoleId,
Index: int32(d.Num),
Price: d.Price,
NeedRoomCard: LotteryRoomCard,
ImageURL: d.ImageURL,
}
if d.WinTs > 0 && d.WinTs > now.Unix() {
// 隐藏未发奖的中奖信息
d.WinCode = ""
d.SnId = 0
d.Name = ""
d.RoleId = 0
}
ret = append(ret, info)
}
sort.Slice(ret, func(i, j int) bool {
if ret[i].StartTs == ret[j].StartTs {
return ret[i].EndTs < ret[j].EndTs
}
return ret[i].StartTs < ret[j].StartTs
})
for k, v := range ret {
v.Index = int32(k + 1)
}
return ret
}
// AddCostRoomCard 添加消耗房卡
// snid: 玩家ID
// n: 消耗房卡数量
// cid: 抽奖配置ID
func (l *LotteryMgr) AddCostRoomCard(plt string, snid int32, n int64) {
// 活动总开关
conf := PlatformMgrSingleton.GetConfig(plt).LotteryConfig
if conf == nil || conf.GetOn() != common.On {
return
}
var notify bool
f := func() {
info := PlayerInfoMgrSingle.Players[snid]
if info == nil {
logger.Logger.Errorf("AddCostRoomCard LotteryData snid:%v not found", snid)
return
}
for _, v := range l.GetList(plt) {
if v == nil || v.GetState() != common.LotteryStateRun {
continue
}
playerLottery := info.Lottery[v.GetId()]
if playerLottery == nil || playerLottery.StartTs != v.GetStartTs() {
playerLottery = &model.Lottery{
SnId: snid,
CId: v.GetId(),
Code: nil,
StartTs: v.GetStartTs(),
CostCard: 0,
ReCostCard: 0,
}
info.Lottery[v.GetId()] = playerLottery
}
lotteryData := l.GetData(plt, v.GetId())
lotteryData.CostCard += n
playerLottery.CostCard += n
n = int64(int(n) + playerLottery.ReCostCard)
playerLottery.ReCostCard = int(n % LotteryRoomCard)
for i := 0; i < int(n)/LotteryRoomCard; i++ {
if v.GetRemainCode() <= 0 {
break
}
code, b := lotteryData.GetCode()
if b {
intCode, _ := strconv.Atoi(code)
playerLottery.Code = append(playerLottery.Code, fmt.Sprintf(lotteryData.Format, intCode))
// 开奖码记录
mq.Write(&model.LotteryCode{
Platform: plt,
SnId: snid,
CId: v.GetId(),
StartTs: v.GetStartTs(),
Code: code,
Index: lotteryData.GetPlayerIndex(),
})
// 机器人发一个
lotteryData.sendRobotCode(1, 1)
notify = true
}
}
}
p := PlayerMgrSington.GetPlayerBySnId(snid)
if notify && p != nil && !p.IsRob {
p.SendToClient(int(welfare.SPacketID_PACKET_NotifyLotteryCode), &welfare.NotifyLotteryCode{})
}
}
p := PlayerMgrSington.GetPlayerBySnId(snid)
if p == nil {
PlayerCacheMgrSingleton.Get(plt, snid, func(item *PlayerCacheItem, b bool, b2 bool) {
if item == nil || item.PlayerData == nil {
logger.Logger.Errorf("AddCostRoomCard snid:%v not found", snid)
return
}
f()
}, false)
return
}
f()
}