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.Second*10, 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) } } } }, OnMiniTimerFunc: func() { for i := range LotteryMgrInst.Data { for k := range LotteryMgrInst.Data[i] { d := LotteryMgrInst.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) } } }, }) } // 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.Debugf("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 !must { if l.isDone { 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("广播中奖信息: %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 now := time.Now() if l.EndTs <= 0 || l.EndTs >= now.Unix() || l.WinCode != "" || 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", lotteryLog) // 开始发奖 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.Done() // 开始发奖 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.WinTs <= 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 remainTime := int64(0) switch { case d.WinTs <= now.Unix(): state = common.LotteryStateOver // 已结束 case d.StartTs > now.Unix(): state = common.LotteryStateNoStart // 未开始 default: state = common.LotteryStateRun // 进行中 remainTime = d.WinTs - now.Unix() } 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, RemainTime: remainTime, } if d.WinTs > 0 && d.WinTs > now.Unix() { // 隐藏未发奖的中奖信息 info.WinCode = "" info.SnId = 0 info.Name = "" info.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 } playerCode := &welfare.NotifyLotteryCode{} 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) var codes []string 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(), }) codes = append(codes, code) // 机器人发一个 lotteryData.sendRobotCode(1, 1) notify = true } } if len(codes) > 0 { playerCode.Info = append(playerCode.Info, &welfare.LotteryInfo{ Id: v.GetId(), StartTs: v.GetStartTs(), EndTs: v.GetEndTs(), WinTs: v.GetWinTs(), Index: int32(LotteryMgrInst.GetIndex(plt, v.GetId())), Codes: codes, }) } } p := PlayerMgrSington.GetPlayerBySnId(snid) if notify && p != nil && !p.IsRob { p.LotteryCode = playerCode } } p := PlayerInfoMgrSingle.Players[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 } PlayerMgrSington.AddPlayer(0, item.PlayerData, nil) f() }, false) return } f() }