package main import ( "fmt" "math/rand" "sort" "sync" "time" nowtool "github.com/jinzhu/now" "mongo.games.com/goserver/core/basic" "mongo.games.com/goserver/core/i18n" "mongo.games.com/goserver/core/logger" "mongo.games.com/goserver/core/module" "mongo.games.com/goserver/core/task" "mongo.games.com/goserver/core/timer" "mongo.games.com/game/common" "mongo.games.com/game/model" "mongo.games.com/game/proto" "mongo.games.com/game/protocol/tournament" webapiproto "mongo.games.com/game/protocol/webapi" "mongo.games.com/game/srvdata" "mongo.games.com/game/webapi" ) // 如果这里比赛类型没有,默认是锦标赛 const ( MatchTypeNormal = iota + 1 // 锦标赛 MatchTypeChampion // 冠军赛/实物赛 MatchTypeVIP // vip比赛 MatchTypePhone // 话费赛 MatchTypeMax // 最大比赛类型 ) const ( MatchSwitchStart = 1 // 开启 MatchSwitchClose = 2 // 关闭 ) const ( MatchUseRobot = 0 // 使用机器人 MatchNotUserRobot = 1 // 关闭机器人 ) const ( JinJi = 0 // 晋级 TaoTai = 1 // 淘汰 DaiDing = 2 // 待定 ) func init() { module.RegisteModule(TournamentMgr, time.Second, 0) common.ClockMgrSingleton.RegisterSinker(TournamentMgr) } var TournamentMgr = NewTournament() type PerRankInfo struct { Name string SnId int32 RankId int32 Grade int32 } type SignInfo struct { signup map[int32]*TmPlayer //玩家Id Platform string MaxCnt int // 最大报名人数 } type SignupInfo struct { Platform string TmId int32 SnId int32 Ts int64 // 报名时间 } type PlayerRoundInfo struct { players []*PlayerMatchContext // 本轮结算信息 ranks []*PlayerMatchContext // 本轮排名 num int // 本轮完成人数 } type Tournament struct { common.BaseClockSinker TypeList map[string]*webapiproto.GameMatchType // 比赛类型列表 平台id:比赛类型列表 GameMatchDateList map[string]map[int32]*webapiproto.GameMatchDate // 比赛配置,platform:比赛场配置id:比赛配置 singleSignupPlayers map[int32]*SignupInfo // 开启机器人时,报名的玩家,玩家Id:报名信息 signupPlayers map[string]map[int32]*SignInfo // 报名的玩家 platform:比赛配置id:报名人 playerWaitStart map[int32]int64 // 等待时间 玩家Id:等待时长秒 matches map[int32]map[int64]*TmMatch // 开始比赛的数据,比赛配置Id:比赛顺序序号:一场开始的比赛数据 players map[int64]map[int32]*PlayerMatchContext // 比赛中玩家 比赛顺序序号:玩家id:玩家信息 roundPlayers map[int64]map[int32]*PlayerRoundInfo // 每轮比赛数据备份 比赛顺序序号:第几轮 finalPerRank map[int64][]*PerRankInfo // 本场比赛排名,每淘汰一位记录一位,最后记录决赛玩家 比赛顺序序号 MatchAwardNum map[string]map[int32]int32 // 比赛配置Id:比赛奖励次數 } func NewTournament() *Tournament { ret := &Tournament{ TypeList: make(map[string]*webapiproto.GameMatchType), GameMatchDateList: make(map[string]map[int32]*webapiproto.GameMatchDate), singleSignupPlayers: make(map[int32]*SignupInfo), signupPlayers: make(map[string]map[int32]*SignInfo), playerWaitStart: make(map[int32]int64), matches: make(map[int32]map[int64]*TmMatch), players: make(map[int64]map[int32]*PlayerMatchContext), roundPlayers: make(map[int64]map[int32]*PlayerRoundInfo), finalPerRank: make(map[int64][]*PerRankInfo), } return ret } // 检查下数据是否合法(主要检查报名人数和晋级方式) n n|...|4|1 func (this *Tournament) checkData(cfg *webapiproto.GameMatchDate) bool { if cfg == nil { return false } l := len(cfg.MatchPromotion) if l < 2 { return false } if cfg.MatchPromotion[0] != cfg.MatchNumebr { //第一位必须和报名人数相同 //return false cfg.MatchNumebr = cfg.MatchPromotion[0] } if cfg.MatchPromotion[l-1] != 1 { //最后一位必须是1 //return false cfg.MatchPromotion[l-1] = 1 } if cfg.MatchPromotion[l-2] != 4 { //倒数第二位必须是4 //return false cfg.MatchPromotion[l-2] = 4 } for i, num := range cfg.MatchPromotion { if i < l-1 { //除了最后一位 if num%4 != 0 { //必须是4的整倍数 return false } //if num <= cfg.MatchPromotion[i+1] { //必须递减 // return false //} } } return true } // 记录淘汰人员 func (this *Tournament) addFinalPlayer(sortId int64, p *PerRankInfo) { if this.finalPerRank[sortId] == nil { this.finalPerRank[sortId] = []*PerRankInfo{} } this.finalPerRank[sortId] = append(this.finalPerRank[sortId], p) } // GetRoundPlayer 查询一场比赛某轮的历史数据 func (this *Tournament) GetRoundPlayer(sortId int64, round int32) *PlayerRoundInfo { _, ok := this.roundPlayers[sortId] if !ok { return nil } if this.roundPlayers[sortId] == nil { this.roundPlayers[sortId] = make(map[int32]*PlayerRoundInfo) } v, ok := this.roundPlayers[sortId][round] if !ok || v == nil { return nil } return v } // GetRemainNum 剩余比赛人数 func (this *Tournament) GetRemainNum(sortId int64) int32 { tm := this.GetTm(sortId) if tm == nil { return 0 } round := this.GetRound(sortId) l := tm.gmd.GetMatchPromotion() if round <= int32(len(l)) { return l[round-1] } return 0 } type MatchPlayerInfo struct { SnId int32 RoleId int32 SkinId int32 Rank int32 Grade int32 } // GetRemainPlayer 未淘汰的人 func (this *Tournament) GetRemainPlayer(sortId int64) []*MatchPlayerInfo { tm := this.GetTm(sortId) if tm == nil { return nil } round := this.GetRound(sortId) useRobot := this.IsRobotOn(tm.gmd) var ret []*MatchPlayerInfo realPlayer := func() { for _, v := range tm.TmPlayer { p := PlayerMgrSington.GetPlayerBySnId(v.SnId) if p != nil { ret = append(ret, &MatchPlayerInfo{ SnId: v.SnId, RoleId: p.GetRoleId(), SkinId: p.Skin.ModId, Grade: 1000, }) } } } robotPlayer := func(n int) { for _, v := range tm.robotGrades[n] { if v.copySnid == 0 { for _, vv := range tm.TmPlayer { p := PlayerMgrSington.GetPlayerBySnId(vv.SnId) if p != nil { ret = append(ret, &MatchPlayerInfo{ SnId: vv.SnId, RoleId: p.GetRoleId(), SkinId: p.Skin.ModId, Grade: v.grade, }) } break } continue } ret = append(ret, &MatchPlayerInfo{ SnId: v.copySnid, RoleId: v.copyRoleId, SkinId: v.CopySkinId, Grade: v.grade, }) } } if round <= 1 { if useRobot { robotPlayer(0) realPlayer() } else { realPlayer() } } else { if useRobot { robotPlayer(int(round - 1)) } else { if this.roundPlayers[sortId] != nil { d := this.roundPlayers[sortId][round-1] if d != nil { for _, v := range d.ranks { ret = append(ret, &MatchPlayerInfo{ SnId: v.copySnid, RoleId: v.copyRoleId, SkinId: v.copySkinId, Grade: v.grade, }) } } } } } return ret } // GetSignUpPlayers 获取报名人员 // id 比赛配置id func (this *Tournament) GetSignUpPlayers(platform string, id int32) map[int32]*TmPlayer { v, ok := this.signupPlayers[platform] if !ok { return nil } vv, ok := v[id] if !ok { return nil } return vv.signup } // UpdateData 更新配置 // init: 通知客户端配置变更 func (this *Tournament) UpdateData(init bool, data *webapiproto.GameMatchDateList) { if data.Platform == "0" { return } // 参数校验 configs := make(map[int32]*webapiproto.GameMatchDate) for _, v := range data.List { if this.checkData(v) { this.FixMatchTimeStamp(v) configs[v.Id] = v } else { logger.Logger.Errorf("GameMatchDate check error: %v", v) } } oldConfigs := this.GameMatchDateList[data.Platform] // 配置修改取消报名 // 1.比赛开启变关闭 // 2.比赛不在时间段内 // 3.机器人开关有修改 // 4.删除配置 for _, v := range oldConfigs { gmd := configs[v.Id] if (gmd != nil && v.MatchSwitch == MatchSwitchStart && gmd.MatchSwitch != v.MatchSwitch) || //配置开启变关闭 (gmd != nil && !this.IsTimeRange(gmd)) || //或者不在时间段内 (gmd != nil && v.UseRobot != gmd.UseRobot) || //或者机器人开关有修改 gmd == nil { //或者删除 signInfo := this.GetSignUpPlayers(data.Platform, v.Id) if signInfo != nil && len(signInfo) > 0 { // 取消报名 this.CancelSignUpAll(data.Platform, v.Id) } } } // 新配置增加更新 for _, v := range configs { oldConfig := oldConfigs[v.Id] if oldConfig == nil || v.MatchSwitch == MatchSwitchStart { if this.signupPlayers[data.Platform] == nil { this.signupPlayers[data.Platform] = make(map[int32]*SignInfo) } this.signupPlayers[data.Platform][v.Id] = &SignInfo{ signup: make(map[int32]*TmPlayer), Platform: data.Platform, MaxCnt: int(v.MatchNumebr), } } if v.MatchSwitch == MatchSwitchClose || !this.IsTimeRange(v) { this.CancelSignUpAll(data.Platform, v.Id) if v.MatchSwitch == MatchSwitchClose { delete(this.signupPlayers[data.Platform], v.Id) } } } this.GameMatchDateList[data.Platform] = configs // 通知平台玩家数据更新 if !init { //todo 优化 for _, v := range PlayerMgrSington.playerOfPlatform[data.Platform] { pack := TournamentMgr.GetSCTMInfosPack(data.Platform, v.AppChannel) v.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMInfos), pack) logger.Logger.Trace("SCTMInfos ", pack) } } } func (this *Tournament) UpdateTypeList(init bool, data *webapiproto.GameMatchType) { if data.Platform == "0" { return } this.TypeList[data.Platform] = data if !init { //todo 优化 for _, v := range PlayerMgrSington.playerOfPlatform[data.Platform] { pack := TournamentMgr.GetSCTMInfosPack(data.Platform, v.AppChannel) v.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMInfos), pack) logger.Logger.Trace("SCTMInfos UpdateTypeList", pack) } } } // GetMatchInfo 比赛配置 // !!!没有sortId会获取最新配置 func (this *Tournament) GetMatchInfo(platform string, tmId int32, sortId int64) *webapiproto.GameMatchDate { if sortId > 0 { v := this.matches[tmId] if v != nil { vv := v[sortId] if vv != nil { return vv.gmd } } return nil } if list, ok := this.GameMatchDateList[platform]; ok { if gmd, ok1 := list[tmId]; ok1 { return gmd } } return nil } // MatchSwitch 比赛开关 func (this *Tournament) MatchSwitch(platform string, tmId int32) bool { return this.IsMatchOn(this.GetMatchInfo(platform, tmId, 0)) } // IsMatchOn 比赛开关 func (this *Tournament) IsMatchOn(match *webapiproto.GameMatchDate) bool { return match != nil && match.MatchSwitch == MatchSwitchStart } // IsUseRobot 是否用机器人 func (this *Tournament) IsUseRobot(platform string, tmId int32, sortId int64) bool { return this.IsRobotOn(this.GetMatchInfo(platform, tmId, sortId)) } // IsRobotOn 是否用机器人 func (this *Tournament) IsRobotOn(match *webapiproto.GameMatchDate) bool { return match != nil && match.UseRobot == MatchUseRobot } // IsTimeRange 判断是否在比赛时间段内 // 在时间段内才能报名 func (this *Tournament) IsTimeRange(gmd *webapiproto.GameMatchDate) bool { if gmd == nil { return false } if gmd.MatchType != MatchTypeChampion { return true } // 冠军赛有时间限制 tNow := time.Now() switch gmd.MatchTimeType { // 0无时效 1重复时间段 2一次性时间段 case 0: return true case 1: week := this.getWeekDay() for _, v := range gmd.MatchTimeWeek { if week == int(v) { if gmd.MatchTimeStamp != nil && len(gmd.MatchTimeStamp) > 1 { this.FixMatchTimeStamp(gmd) startStamp := gmd.MatchTimeStamp[0] endStamp := gmd.MatchTimeStamp[1] if tNow.Unix() >= startStamp && tNow.Unix() <= endStamp { return true } } return false } } case 2: if gmd.MatchTimeStamp != nil && len(gmd.MatchTimeStamp) > 1 { startStamp := gmd.MatchTimeStamp[0] endStamp := gmd.MatchTimeStamp[1] if tNow.Unix() >= startStamp && tNow.Unix() <= endStamp { return true } } } return false } // IsOutTime 是否过期,一次性比赛才有过期,一次性冠军赛 func (this *Tournament) IsOutTime(gmd *webapiproto.GameMatchDate) bool { if !this.IsMatchOn(gmd) { return true } if gmd.MatchType == MatchTypeChampion { //冠军赛的一次性时间段才有过期可能 switch gmd.MatchTimeType { // 0无时效 1重复时间段 2一次性时间段 case 2: tNow := time.Now() if gmd.MatchTimeStamp != nil && len(gmd.MatchTimeStamp) > 1 { endStamp := gmd.MatchTimeStamp[1] if tNow.Unix() >= endStamp { //当前时间大于截止时间 return true } } } } return false } // GetAllMatchInfo 所有比赛配置数据 func (this *Tournament) GetAllMatchInfo(platform string) map[int32]*webapiproto.GameMatchDate { if list, ok := this.GameMatchDateList[platform]; ok { return list } return nil } // IsMatching 判断是否在比赛中 func (this *Tournament) IsMatching(snId int32) bool { if this.players != nil { for _, v := range this.players { if v != nil { for k := range v { if k == snId { return true } } } } } return false } // IsMatchWaiting 判断是否在匹配中,已报名 func (this *Tournament) IsMatchWaiting(platform string, snId int32) (bool, int32) { // 未使用机器人 for k, v := range this.signupPlayers[platform] { if v.signup != nil { if _, ok := v.signup[snId]; ok { return ok, k } } } for k, v := range this.matches { for _, vv := range v { if _, ok := vv.TmPlayer[snId]; ok { return true, k } } } // 使用机器人了 if v, ok := this.singleSignupPlayers[snId]; ok { return true, v.TmId } return false, 0 } // signUpCost 报名消耗 // tmId 比赛配置id // cost true报名、false取消报名 // 报名费用 0成功 3道具不足 5金币不足 6钻石不足 7免费次数不足 func (this *Tournament) signUpCost(p *Player, tmId int32, cost bool) (bool, int32) { logger.Logger.Tracef("signUpCost 比赛id:%v 玩家:%v 报名:%v", tmId, p.SnId, cost) gmd := this.GetMatchInfo(p.Platform, tmId, 0) if gmd == nil || p.IsRob { return true, 0 } // 真人费用检测 // VIP比赛场 if gmd.MatchType == MatchTypeVIP { freeTimes := p.GetMatchFreeTimes() // VIP比赛场免费次数 freeTimes += p.GetSkillAdd(common.SkillIdVipTimes) // 皮肤技能加成次数 logger.Logger.Trace("报名费用免费次数: freeTimes: ", freeTimes, " p.VipMatchTimes: ", p.VipMatchTimes) if freeTimes > 0 { if cost { if p.VipMatchTimes < freeTimes { //参与次数<免费次数 p.VipMatchTimes++ } else { logger.Logger.Trace("免费次数不足1") return false, int32(tournament.SignRaceCode_OPRC_Free) } } else { if p.VipMatchTimes > 0 { p.VipMatchTimes-- } else { p.VipMatchTimes = 0 } } } else { logger.Logger.Trace("免费次数不足2") return false, int32(tournament.SignRaceCode_OPRC_Free) } } // 金币 if gmd.SignupCostCoin > 0 { logger.Logger.Tracef("比赛场报名消耗金币 报名:%v 金币:%v", cost, gmd.SignupCostCoin) if cost { if p.Coin < gmd.SignupCostCoin { //金币不足 logger.Logger.Trace("金币不足") return false, int32(tournament.SignRaceCode_OPRC_Coin) } else { p.AddCoin(-gmd.SignupCostCoin, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名消耗") } } else { p.AddCoin(gmd.SignupCostCoin, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名退还") } } // 钻石 if gmd.SignupCostDiamond > 0 { logger.Logger.Tracef("比赛场报名消耗钻石 报名:%v 钻石:%v", cost, gmd.SignupCostDiamond) if cost { if p.Diamond < gmd.SignupCostDiamond { //钻石不足 logger.Logger.Trace("钻石不足") return false, int32(tournament.SignRaceCode_OPRC_Diamond) } else { p.AddDiamond(-gmd.SignupCostDiamond, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名消耗") } } else { p.AddDiamond(gmd.SignupCostDiamond, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名退还") } } // 道具 if gmd.SignupCostItem != nil && gmd.SignupCostItem.ItemNum > 0 { logger.Logger.Tracef("比赛场报名消耗道具 报名:%v 道具:%v", cost, gmd.SignupCostItem.String()) item := BagMgrSingleton.GetItem(p.SnId, gmd.SignupCostItem.ItemId) if item != nil { gameId := int64(0) gf := PlatformMgrSingleton.GetGameFree(p.Platform, gmd.GameFreeId) if gf != nil && gf.GetDbGameFree() != nil { gameId = int64(gf.GetDbGameFree().GameId) } if cost { if item.ItemNum < gmd.SignupCostItem.ItemNum { logger.Logger.Trace("道具不足") return false, int32(tournament.SignRaceCode_OPRC_NoItem) } else { BagMgrSingleton.AddItems(&model.AddItemParam{ Platform: p.Platform, SnId: p.SnId, Change: []*model.Item{ { ItemId: item.ItemId, ItemNum: -gmd.SignupCostItem.ItemNum, }, }, GainWay: common.GainWay_MatchSignup, Operator: "player", Remark: gmd.MatchName + "-报名消耗", GameId: gameId, GameFreeId: int64(gmd.GameFreeId), }) } } else { BagMgrSingleton.AddItems(&model.AddItemParam{ Platform: p.Platform, SnId: p.SnId, Change: []*model.Item{ { ItemId: item.ItemId, ItemNum: gmd.SignupCostItem.ItemNum, }, }, GainWay: common.GainWay_MatchSignup, Operator: "player", Remark: gmd.MatchName + "-报名退还", GameId: gameId, GameFreeId: int64(gmd.GameFreeId), }) } } else { logger.Logger.Trace("道具不足") return false, int32(tournament.SignRaceCode_OPRC_NoItem) } } return true, 0 } // SignUp 报名 // 0成功 1重复报名 2比赛没有开启 3道具不足 4不在报名时间段 5金币不足 6钻石不足 7免费次数不足 func (this *Tournament) SignUp(tmId int32, p *Player) (bool, int32) { logger.Logger.Tracef("报名 比赛id:%v 玩家:%v", tmId, p.SnId) // 开启 info := this.GetMatchInfo(p.Platform, tmId, 0) if !this.IsMatchOn(info) { return false, int32(tournament.SignRaceCode_OPRC_Close) } // 报名时间 if !this.IsTimeRange(info) { return false, int32(tournament.SignRaceCode_OPRC_Time) } // 已报名 isWaiting, _ := this.IsMatchWaiting(p.Platform, p.SnId) if this.IsMatching(p.SnId) || isWaiting { return false, int32(tournament.SignRaceCode_OPRC_Repeat) } if this.GetMatchAwardNum(p.Platform, tmId) <= 0 { return false, int32(tournament.SignRaceCode_OPRC_NoAward) } // 扣报名费 ok, code := this.signUpCost(p, tmId, true) if !ok { return false, code } if this.IsRobotOn(info) { // 开启机器人时 signupInfo := &SignupInfo{ Platform: p.Platform, TmId: tmId, SnId: p.SnId, Ts: time.Now().Unix(), } this.singleSignupPlayers[p.SnId] = signupInfo } else { // 不开机器人时 var platform = p.Platform signInfo, ok := this.signupPlayers[platform][tmId] if !ok { signInfo = &SignInfo{ signup: make(map[int32]*TmPlayer), Platform: platform, MaxCnt: int(info.MatchNumebr), } this.signupPlayers[platform][tmId] = signInfo } _, ok = signInfo.signup[p.SnId] if ok { return false, int32(tournament.SignRaceCode_OPRC_Repeat) } n := len(signInfo.signup) + 1 signInfo.signup[p.SnId] = &TmPlayer{SnId: p.SnId, seq: n} logger.Logger.Tracef("没使用机器人,真人报名,报名人数 %v 玩家:%v", n, p.SnId) } //(报名人数/20,报名人数/10) minTime := info.MatchNumebr / 20 maxTime := info.MatchNumebr / 10 if info.MatchNumebr > 20 { this.playerWaitStart[p.SnId] = int64(minTime + rand.Int31n(maxTime-minTime)) } else { this.playerWaitStart[p.SnId] = 1 } return true, 0 } // CancelSignUp 取消玩家报名 func (this *Tournament) CancelSignUp(platform string, tmid, snid int32) { delete(this.playerWaitStart, snid) if this.signupPlayers[platform] != nil && this.signupPlayers[platform][tmid] != nil { signInfo := this.signupPlayers[platform][tmid] if _, ok := signInfo.signup[snid]; ok { p := PlayerMgrSington.GetPlayerBySnId(snid) if p != nil && p.scene == nil { //退费 this.signUpCost(p, tmid, false) delete(this.signupPlayers[platform][tmid].signup, snid) //通知取消报名 pack := &tournament.SCSignRace{ OpCode: 1, RetCode: 0, } p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCSignRace), pack) logger.Logger.Trace("SCSignRace ", pack) return } } } if v, ok := this.singleSignupPlayers[snid]; ok { p := PlayerMgrSington.GetPlayerBySnId(snid) if p != nil && p.scene == nil { //退费 this.signUpCost(p, v.TmId, false) delete(this.singleSignupPlayers, snid) //通知取消报名 pack := &tournament.SCSignRace{ OpCode: 1, RetCode: 0, } p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCSignRace), pack) logger.Logger.Trace("SCSignRace ", pack) return } } } // CancelSignUpAll 取消所有报名 func (this *Tournament) CancelSignUpAll(platform string, tmId int32) { if this.signupPlayers[platform] != nil && this.signupPlayers[platform][tmId] != nil { for _, tmp := range this.signupPlayers[platform][tmId].signup { this.CancelSignUp(platform, tmId, tmp.SnId) } } if this.singleSignupPlayers != nil { for _, signupInfo := range this.singleSignupPlayers { if signupInfo.TmId == tmId { this.CancelSignUp(signupInfo.Platform, tmId, signupInfo.SnId) } } } } // Quit 如果正在等待比赛,退赛 func (this *Tournament) Quit(platform string, snid int32) { logger.Logger.Tracef("TournamentMgr.Quit: snId:%d", snid) isWaiting, tmid := this.IsMatchWaiting(platform, snid) if isWaiting && !this.IsMatching(snid) { this.CancelSignUp(platform, tmid, snid) } } // ForceQuit 强制退赛 func (this *Tournament) ForceQuit(platform string, snId int32) { logger.Logger.Tracef("TournamentMgr.ForceQuit: snId:%d", snId) this.Quit(platform, snId) var info *PlayerMatchContext var ok bool for _, v := range this.players { info, ok = v[snId] if ok { break } } if info != nil && info.tm != nil { this.StopMatch(info.tm.TMId, info.tm.SortId) MatchSceneMgrSingleton.MatchStop(info.tm) } } // CanStart 是否开赛 // 没有开机器人走这个判断 func (this *Tournament) CanStart(platform string, tmId int32) bool { if this.signupPlayers != nil && this.signupPlayers[platform] != nil { if signInfo, ok := this.signupPlayers[platform][tmId]; ok { matchInfo := this.GetMatchInfo(signInfo.Platform, tmId, 0) if matchInfo != nil { n := 0 for _, v := range signInfo.signup { p := PlayerMgrSington.GetPlayerBySnId(v.SnId) if p == nil || p.scene != nil { //人不在或者在游戏内 this.CancelSignUp(platform, tmId, v.SnId) break } n++ } logger.Logger.Trace("TournamentMgr.CanStart: n: ", n, " matchInfo: ", matchInfo) needNum := matchInfo.MatchNumebr / 4 * 4 //对4向下取整防止后台配置数据不是4的倍数 //所有人都准备好了 if needNum == int32(n) { //人满 开始比赛 return true } } } } return false } // Start 开赛 func (this *Tournament) Start(platform string, tmId int32) { if this.signupPlayers[platform] == nil { this.signupPlayers[platform] = make(map[int32]*SignInfo) } if this.matches[tmId] == nil { this.matches[tmId] = make(map[int64]*TmMatch) } matchInfo := this.GetMatchInfo(platform, tmId, 0) signInfo := this.signupPlayers[platform][tmId] if matchInfo == nil || signInfo == nil || len(signInfo.signup) == 0 { return } tm := NewTmMatch(platform, matchInfo, signInfo.signup) this.matches[tmId][tm.SortId] = tm logger.Logger.Tracef("开赛:%+v, 难度:%v", tm, tm.gml) tm.Start() // 开始比赛,清除报名数据 for _, v := range signInfo.signup { delete(this.singleSignupPlayers, v.SnId) TaskSubjectSingleton.Touch(common.TaskTypeJoinMatch, &TaskData{ SnId: v.SnId, GameID: int(tm.dbGameFree.GetGameId()), GameFreeID: tm.dbGameFree.GetId(), Num: 1, }) if matchInfo.GetSignupCostDiamond() > 0 { TaskSubjectSingleton.Touch(common.TaskTypeCostDiamond, &TaskData{ SnId: v.SnId, Num: matchInfo.GetSignupCostDiamond(), }) } } this.signupPlayers[platform][tmId].signup = make(map[int32]*TmPlayer) } // StopMatch 比赛结束 func (this *Tournament) StopMatch(tmId int32, sortId int64) { logger.Logger.Tracef("StopMatch:%v, %v", tmId, sortId) //房间清理 if this.matches[tmId] != nil && this.matches[tmId][sortId] != nil { tm := this.matches[tmId][sortId] matchLog := this.MakeMatchLog(tm.Platform, tmId, sortId) this.saveMatchLog(matchLog) tm.Stop() } if this.matches[tmId] != nil { delete(this.matches[tmId], sortId) } //数据清理 delete(this.players, sortId) delete(this.roundPlayers, sortId) delete(this.finalPerRank, sortId) } // GetTm 获取比赛信息 func (this *Tournament) GetTm(sortId int64) *TmMatch { for _, v := range this.matches { if v == nil { continue } if vv, ok := v[sortId]; ok { return vv } } return nil } // CreatePlayerMatchContext 创建玩家比赛信息 func (this *Tournament) CreatePlayerMatchContext(p *Player, m *TmMatch, seq int) *PlayerMatchContext { roleId := int32(2000001) if p.Roles != nil { roleId = p.Roles.ModId } mc := NewMatchContext(p, m, 1000, p.SnId, 1, roleId, p.Skin.ModId, seq) if mc != nil { if this.players[m.SortId] == nil { this.players[m.SortId] = make(map[int32]*PlayerMatchContext) } this.players[m.SortId][p.SnId] = mc p.matchCtx = mc return mc } return nil } // 是否淘汰 func (this *Tournament) isOut(sortId int64, snid int32) bool { if this.finalPerRank[sortId] != nil { for _, info := range this.finalPerRank[sortId] { if info.SnId == snid { return true } } } return false } // getRank 获取本轮排名 // 不适用机器人时 func (this *Tournament) getRank(sortId int64, round, snid int32, isFinals bool) int32 { if _, ok := this.roundPlayers[sortId]; ok { if rps, ok1 := this.roundPlayers[sortId][round]; ok1 && rps != nil { MatchContextSlice(rps.ranks).Sort(isFinals) for _, rp := range rps.ranks { if rp.p.SnId == snid { return rp.rank } } } } return 0 } // GetRank 获取排名 func (this *Tournament) GetRank(sortId int64, snid int32) int32 { tm := this.GetTm(sortId) if tm == nil { return 0 } round := this.GetRound(sortId) useRobot := this.IsRobotOn(tm.gmd) robotRankFunc := func(n int, snid int32) int32 { rank := int32(0) for _, v := range tm.robotGrades[n] { if v.copySnid == snid { return v.rank } if v.copySnid == 0 { rank = v.rank } } return rank } playerRankFunc := func(n int, snid int32) int32 { d := this.GetRoundPlayer(sortId, int32(n)) if d != nil { for _, v := range d.players { if v.p.SnId == snid { return v.rank } } } d = this.GetRoundPlayer(sortId, int32(n-1)) if d != nil { for _, v := range d.players { if v.p.SnId == snid { return v.rank } } } return 0 } if round <= 1 { d := tm.TmPlayer[snid] if d != nil { return int32(d.seq) } if useRobot { n := 0 for _, v := range tm.TmPlayer { n = v.seq - 1 break } for k, v := range tm.robotGrades[0] { if v.copySnid == snid { if k < n { return int32(k) } return int32(k + 2) } } } return 0 } else { if useRobot { return robotRankFunc(int(round-1), snid) } else { return playerRankFunc(int(round-1), snid) } } } // UpdateMatchInfo 玩家比赛结束 更新积分 func (this *Tournament) UpdateMatchInfo(p *Player, sortId int64, grade, isWin int32, matchRobotGrades map[int32]int32) { logger.Logger.Tracef("UpdateMatchInfo: sortId:%v, grade:%v, isWin: %v, matchRobotGrades:%v", sortId, grade, isWin, matchRobotGrades) if this.GetTm(sortId) == nil { logger.Logger.Tracef("UpdateMatchInfo: 比赛已经结束") return } if _, ok := this.players[sortId]; !ok { this.players[sortId] = make(map[int32]*PlayerMatchContext) } info, ok := this.players[sortId][p.SnId] if !ok { return } if _, ok = this.roundPlayers[sortId]; !ok { this.roundPlayers[sortId] = make(map[int32]*PlayerRoundInfo) } if this.roundPlayers[sortId][info.round] == nil { this.roundPlayers[sortId][info.round] = &PlayerRoundInfo{} } // 积分 info.grade = grade // 完成人数 this.roundPlayers[sortId][info.round].num++ // 输赢统计 if info.record == nil { info.record = make(map[int32]int32) } info.record[isWin]++ //轮数增加 info.round++ info.gaming = false // 备份本轮比赛结算信息 mc := *info if this.roundPlayers[sortId][info.round] == nil { this.roundPlayers[sortId][info.round] = &PlayerRoundInfo{} } this.roundPlayers[sortId][info.round].players = append(this.roundPlayers[sortId][info.round].players, &mc) // 计算排名用 this.roundPlayers[sortId][info.round].ranks = append(this.roundPlayers[sortId][info.round].ranks, &mc) // 检查是否开始下一局 if this.IsRobotOn(info.tm.gmd) { this.NextRoundStartSingle(sortId, info, matchRobotGrades) } else { this.NextRoundStart(sortId, info) } } func (this *Tournament) stopMatch(matchId int32, sortId int64) (isOver bool) { if this.players[sortId] != nil { hasReal := false for snId, context := range this.players[sortId] { if !context.p.IsRob && !this.isOut(sortId, snId) { // 有真人没有淘汰 hasReal = true break } } //没有真人比赛解散 if !hasReal { isOver = true logger.Logger.Trace("没有真人比赛解散") this.StopMatch(matchId, sortId) } } return } // NextRoundStartSingle 下一轮是否开始 // 开启机器人时使用 func (this *Tournament) NextRoundStartSingle(sortId int64, playerCtx *PlayerMatchContext, matchRobotGrades map[int32]int32) { logger.Logger.Tracef("NextRoundStartSingle 当前第 %v 轮", playerCtx.round) info := this.GetRoundPlayer(sortId, playerCtx.round) if info == nil { return } gmd := playerCtx.tm.gmd // 需要晋级的人数 promotionNum := gmd.MatchPromotion[playerCtx.round] updateRobotGrades := func() { round := playerCtx.round if promotionNum == 1 { // 决赛 round = playerCtx.round - 1 } arr := playerCtx.tm.robotGrades[int(round)] // 修改陪真人玩的机器人积分 if matchRobotGrades != nil { logger.Logger.Trace("和真人比赛的机器人使用真实积分: ", matchRobotGrades) for _, gradeInfo := range arr { if grade, ok := matchRobotGrades[gradeInfo.copySnid]; ok { gradeInfo.grade = grade } } } findMe := false for _, v := range arr { if v.copySnid == 0 { // 真人 v.grade = playerCtx.grade findMe = true break } } if !findMe { gradeInfo := &TmGradeInfo{ grade: playerCtx.grade, copySnid: 0, } arr = append(arr, gradeInfo) // 添加了真人数据 } sort.Slice(arr, func(i, j int) bool { if arr[i].grade == arr[j].grade { // 真人在前 if arr[i].copySnid == 0 { return true } if arr[j].copySnid == 0 { return false } } return arr[i].grade > arr[j].grade }) for k, v := range arr { v.rank = int32(k + 1) } playerCtx.tm.robotGrades[int(round)] = arr for _, info := range arr { logger.Logger.Tracef("NextRoundStart_Single 本轮积分排名 round:%v Snid:%v Grade:%v copyLv:%v copyRoleId:%v rank:%v", playerCtx.round, info.copySnid, info.grade, info.copyLv, info.copyRoleId, info.rank) } // 获取排名 index := int32(0) //晋级淘汰 for i, v := range arr { if playerCtx.grade >= v.grade { index = int32(i) + 1 break } } if promotionNum != 1 { if int(promotionNum) < len(arr) { arr = arr[:promotionNum] } playerCtx.tm.robotGrades[int(round)] = arr } if index == 0 { // 是最后一名 index = gmd.MatchPromotion[round] } playerCtx.rank = index } if promotionNum != 1 { // 非决赛 if playerCtx.tm.robotGrades == nil || playerCtx.tm.robotGrades[int(playerCtx.round)] == nil { playerCtx.tm.CreateRobotGrades(int(playerCtx.round)) } if playerCtx.tm.robotGrades != nil && playerCtx.tm.robotGrades[int(playerCtx.round)] != nil { updateRobotGrades() if playerCtx.rank <= promotionNum { // 晋级 this.sendPromotionInfo(playerCtx, sortId, JinJi, false, false) //晋级 // 开始下一轮 mct := []*PlayerMatchContext{playerCtx} finals := false if playerCtx.round == int32(len(gmd.MatchPromotion)-2) { finals = true } timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { MatchSceneMgrSingleton.NewRoundStart(playerCtx.tm, mct, finals, playerCtx.round+1) return true }), nil, time.Second*7, 1) } else { // 淘汰 this.sendPromotionInfo(playerCtx, sortId, TaoTai, true, false) //淘汰 } } } else { // 比赛结束 updateRobotGrades() outCode := TaoTai if playerCtx.rank == 1 { outCode = JinJi } this.sendPromotionInfo(playerCtx, sortId, outCode, true, true) //晋级 logger.Logger.Trace("比赛结束!!! ") // 比赛结束 this.StopMatch(playerCtx.tm.TMId, sortId) } } // NextRoundStart 下一轮是否开始 // 关闭机器时使用 func (this *Tournament) NextRoundStart(sortId int64, playerCtx *PlayerMatchContext) { logger.Logger.Tracef("NextRoundStart 当前第 %v 轮", playerCtx.round) info := this.GetRoundPlayer(sortId, playerCtx.round) if info == nil { return } gmd := playerCtx.tm.gmd // 需要晋级的人数 promotionNum1 := int(gmd.MatchPromotion[playerCtx.round-1]) promotionNum := int(gmd.MatchPromotion[playerCtx.round]) n := len(info.players) outNum := promotionNum1 - promotionNum if promotionNum != 1 { // 非决赛淘汰后开始配桌 // 已经晋级的人数减去一桌之后 剩余人数还能够满足本轮淘汰 logger.Logger.Tracef("非决赛开始淘汰晋级 等待人数: %v 淘汰人数:%v", n, outNum) if n-4 >= outNum { // 提前晋级的开始凑桌 MatchContextSlice(info.players).Sort(false) // 晋级 var ps []*PlayerMatchContext finals := false // 是否最后一局 meIn := false // 自己是否晋级 for i := 0; i < n-outNum; i++ { this.sendPromotionInfo(info.players[i], sortId, JinJi, false, false) //晋级 v := *info.players[i] v.rank = this.getRank(sortId, info.players[i].round, info.players[i].p.SnId, false) ps = append(ps, &v) logger.Logger.Tracef("凑桌成功:%+v", v) if !finals && v.round == int32(len(gmd.MatchPromotion)-2) { finals = true } if v.p.SnId == playerCtx.p.SnId { meIn = true } } info.players = info.players[len(ps):] willOut := false if promotionNum1 == this.GetRoundPlayer(sortId, playerCtx.round-1).num { // 最后一个人打完了,确定要淘汰的人 willOut = true } else { if !meIn { //自己暂时没晋级 this.sendPromotionInfo(playerCtx, sortId, DaiDing, false, false) //待定 } } isOver := false if willOut { for _, v := range info.players { logger.Logger.Tracef("淘汰 %+v", *v) this.sendPromotionInfo(v, sortId, TaoTai, true, false) //淘汰 //真人被淘汰,如果剩下的都是机器人,比赛解散 if !v.p.IsRob { isOver = this.stopMatch(playerCtx.tm.TMId, sortId) } } } if !isOver { timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { MatchSceneMgrSingleton.NewRoundStart(playerCtx.tm, ps, finals, playerCtx.round+1) return true }), nil, time.Second*7, 1) } } else { this.sendPromotionInfo(playerCtx, sortId, DaiDing, false, false) //待定 } } else { if len(info.players) == 4 { MatchContextSlice(info.players).Sort(true) for _, mc := range info.players { outCode := TaoTai if mc.rank == 1 { outCode = JinJi } this.sendPromotionInfo(mc, sortId, outCode, true, true) //晋级 } logger.Logger.Trace("比赛结束!!! ") // 比赛结束 this.StopMatch(playerCtx.tm.TMId, sortId) } } } // 发送晋级信息 // outCode 0晋级 1淘汰 2待定 // isOver 玩家比赛结束 // isFinals 比赛结束 func (this *Tournament) sendPromotionInfo(mc *PlayerMatchContext, sortId int64, outCode int, isOver, isFinals bool) { logger.Logger.Tracef("sendPromotionInfo %+v sortId:%v outCode:%v isOver:%v isFinals:%v", mc, sortId, outCode, isOver, isFinals) if mc == nil || mc.tm == nil || mc.tm.gmd == nil { return } // 淘汰或最后一局,记录排名 if outCode == TaoTai || isFinals { pri := &PerRankInfo{ Name: mc.p.Name, SnId: mc.p.SnId, RankId: mc.rank, Grade: mc.grade, } this.addFinalPlayer(sortId, pri) } if !this.IsRobotOn(mc.tm.gmd) { // 不使用机器人 mc.rank = this.getRank(sortId, mc.round, mc.p.SnId, isFinals) } // 通知晋级信息:0.晋级等待匹配 1.失败退出 2.等待判断是否晋级 pack := &tournament.SCPromotionInfo{ RetCode: int32(outCode), Round: mc.round, RankId: mc.rank, RoundCoin: 100, //暂时用不到先写死 Record: mc.record, MatchId: mc.tm.TMId, MatchPromotion: mc.tm.gmd.GetMatchPromotion(), MatchName: mc.tm.gmd.GetMatchName(), } // 真人发奖 if !mc.p.IsRob && isOver { for _, award := range mc.tm.gmd.Award { if mc.rank >= award.UpLimit && mc.rank <= award.DownLimit { //上下限是反的,我也是醉了 rankAward := &tournament.RankAward{ Coin: proto.Int64(award.Coin), Diamond: proto.Int64(award.Diamond), } if award.ItemId != nil { for _, info := range award.ItemId { item := &tournament.ItemInfo{ ItemId: proto.Int32(info.ItemId), ItemNum: proto.Int32(int32(info.ItemNum)), Name: proto.String(info.Name), } rankAward.ItemInfo = append(rankAward.ItemInfo, item) } } pack.RankAward = rankAward if mc.rank == 1 { if this.MatchAwardNum == nil { this.MatchAwardNum = make(map[string]map[int32]int32) } if this.MatchAwardNum[mc.p.Platform] == nil { this.MatchAwardNum[mc.p.Platform] = make(map[int32]int32) } this.MatchAwardNum[mc.p.Platform][mc.tm.gmd.Id] += 1 } } } } sendFunc := func() { outStr := "晋级" switch outCode { case 1: outStr = "淘汰" case 2: outStr = "待定" } ok := mc.p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCPromotionInfo), pack) logger.Logger.Trace("sendPromotionInfo: ", outStr, " snid: ", mc.p.SnId, " pack: ", pack) if ok && !mc.p.IsRob && isOver { if pack.RankAward != nil { if pack.RankAward.Coin != 0 { //金币 mc.p.AddCoin(pack.RankAward.Coin, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励") } if pack.RankAward.Diamond != 0 { //钻石 mc.p.AddDiamond(pack.RankAward.Diamond, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励") } if pack.RankAward.ItemInfo != nil { for _, info := range pack.RankAward.ItemInfo { if info.ItemNum <= 0 { continue } BagMgrSingleton.AddItems(&model.AddItemParam{ Platform: mc.p.Platform, SnId: mc.p.SnId, Change: []*model.Item{ { ItemId: info.ItemId, ItemNum: int64(info.ItemNum), }, }, GainWay: common.GainWay_MatchSystemSupply, Operator: "system", Remark: mc.tm.gmd.MatchName + "排名奖励", GameId: int64(mc.tm.dbGameFree.GetGameId()), GameFreeId: int64(mc.tm.dbGameFree.GetId()), }) } } } } if isOver { // 自己比赛结束 delete(this.players[sortId], mc.p.SnId) //真人被淘汰,如果剩下的都是机器人,比赛解散 if !mc.p.IsRob { this.stopMatch(mc.tm.TMId, sortId) } } } // 获取兑换码奖品 var has bool wg := new(sync.WaitGroup) if pack.GetRankAward() != nil { for _, v := range pack.GetRankAward().GetItemInfo() { data := srvdata.GameItemMgr.Get(mc.p.Platform, v.ItemId) if data != nil && data.GetType() == common.ItemTypePhoneCode { has = true item := v // 并发需要复制 wg.Add(1) // 兑换码奖品 var err error var newMsg *model.Message res := &webapiproto.SAGetMatchAwardCode{} task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { // 获取兑换码 pack := &webapiproto.ASGetMatchAwardCode{ Platform: mc.p.Platform, Snid: mc.p.SnId, ItemID: data.GetId(), Money: data.GetNum(), Tel: mc.p.Tel, CardType: mc.tm.gmd.CardType, Remark: "比赛场获得奖励", } logger.Logger.Trace("GetMatchAwardCode ", pack) var buff []byte buff, err = webapi.APIGetMatchAwardCode(common.GetAppId(), pack) if err != nil { return err } if err = proto.Unmarshal(buff, res); err != nil { return err } if res.GetCode() == "" || res.GetTag() != webapiproto.TagCode_SUCCESS { return nil } var params []string for range []string{"zh", "vi", "en", "kh"} { params = append(params, res.GetMoney(), res.GetCode()) // 金额,兑换码 } // 发送邮件 title := i18n.Tr("languages", "MatchAwardTitle") content := i18n.Tr("languages", "MatchAward", params) newMsg = model.NewMessage("", 0, "", mc.p.SnId, model.MSGTYPE_RANK_REWARD, title, content, 0, 0, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", nil, mc.p.Platform, model.HallTienlen, nil) err := model.InsertMessage(mc.p.Platform, newMsg) if err != nil { logger.Logger.Errorf("发送邮件失败 snid:%v itemID:%v err:%v", mc.p.SnId, data.Id, err) return err } return nil }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { defer wg.Done() if err != nil || res.GetCode() == "" || res.GetTag() != webapiproto.TagCode_SUCCESS { logger.Logger.Errorf("获取兑换码失败 snid:%v itemID:%v res:%v err:%v", mc.p.SnId, data.Id, res, err) return } item.Code = res.GetCode() item.Msg = res.GetMoney() p := PlayerMgrSington.GetPlayerBySnId(mc.p.SnId) if p != nil { p.AddMessage(newMsg) //比赛场记录话费产出log awardLog := model.AnnouncerLog{ Platform: p.Platform, Snid: p.SnId, Name: p.Name, Phone: p.Tel, ItemId: data.Id, //获得物品ID TypeId: int32(1), } AwardLogMgr.UpdateAnnouncerLog(awardLog) AwardLogMgr.UpdateAwardLog(p.Platform, item.ItemId, 1) } }), fmt.Sprintf("RankAward%d", mc.p.SnId)).Start() } } } if !has { sendFunc() return } task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { wg.Wait() return nil }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { sendFunc() }), "sendPromotionInfo").Start() } // 更新段位 //func (this *Tournament) CheckAddMatchSeasonLv(mc *PlayerMatchContext) { // if mc == nil { // return // } // platform := mc.p.Platform // if platform == DefaultPlatform { // return // } // rank := mc.rank // maxPlayerNum := mc.tm.gmd.MatchNumebr // upLine := maxPlayerNum * 33 / 100 // downLine := maxPlayerNum * 67 / 100 // snid := mc.p.SnId // ms := MatchSeasonMgrSington.GetMatchSeason(mc.p.SnId) // msid := MatchSeasonMgrSington.GetMatchSeasonId(platform) // if msid == nil { // MatchSeasonMgrSington.UpdateMatchSeasonId(platform) // } // if ms == nil { // task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { // ret, err := model.QueryMatchSeasonBySnid(platform, snid) // if err != nil { // return nil // } // return ret // }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { // var ret *model.MatchSeason // dirty := false // seasonId := int32(1) // if msid != nil { // seasonId = msid.SeasonId // } // if data == nil || data.(*model.MatchSeason) == nil { // player := PlayerMgrSington.GetPlayerBySnId(snid) // if player != nil { // ret = model.NewMatchSeason(player.Platform, snid, player.Name, seasonId, 1) // dirty = true // } else { // logger.Logger.Error("CSTMSeasonInfoHandler error: player == nil or msi == nil", snid) // } // } else { // ret = data.(*model.MatchSeason) // if ret.SeasonId < seasonId { //不同赛季段位继承 // num := seasonId - ret.SeasonId // finalLv := ret.Lv // for i := 0; i < int(num); i++ { //继承几次 // if i == int(num)-1 { //上个赛季 // ret.LastLv = finalLv // } // finalLv = MatchSeasonMgrSington.MatchSeasonInherit(finalLv) // } // ret.Lv = finalLv // ret.SeasonId = seasonId // ret.IsAward = false // dirty = true // } // } // ms = MatchSeasonMgrSington.exchangeModel2Cache(ret) // ms.dirty = dirty // MatchSeasonMgrSington.SetMatchSeason(ms) // if rank <= upLine { //加分 // MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 1, dirty) // } else if rank >= downLine && ms.Lv > 75 { //白银以上才触发减分 // MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, -1, dirty) // } else { // MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 0, dirty) // } // })).StartByFixExecutor("SnId:" + strconv.Itoa(int(snid))) // } else { // dirty := false // if ms.SeasonId < msid.SeasonId { //不同赛季段位继承 // num := msid.SeasonId - ms.SeasonId // finalLv := ms.Lv // for i := 0; i < int(num); i++ { //继承几次 // if i == int(num)-1 { //上个赛季 // ms.LastLv = finalLv // } // finalLv = MatchSeasonMgrSington.MatchSeasonInherit(finalLv) // } // ms.Lv = finalLv // ms.SeasonId = msid.SeasonId // ms.IsAward = false // ms.UpdateTs = time.Now().Unix() // ms.dirty = true // dirty = true // MatchSeasonMgrSington.SetMatchSeason(ms) // } // if rank <= upLine { //加分 // MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 1, dirty) // } else if rank >= downLine && ms.Lv > 75 { //白银以上才触发减分 // MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, -1, dirty) // } else { // MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 0, dirty) // } // } //} // GetSCTMInfosPack 发送比赛配置数据 func (this *Tournament) GetSCTMInfosPack(platform, channelName string) *tournament.SCTMInfos { pack := &tournament.SCTMInfos{} matchInfo := this.GetAllMatchInfo(platform) if matchInfo != nil { for id, info := range matchInfo { if info.MatchSwitch == 1 && !this.IsOutTime(info) && common.InMatchChannel(info.OnChannelName, channelName) { awardNum := this.GetMatchAwardNum(platform, info.Id) tMInfo := &tournament.TMInfo{ Id: proto.Int32(id), GameFreeId: info.GameFreeId, MatchType: info.MatchType, MatchName: info.MatchName, MatchNumebr: info.MatchNumebr, MatchSwitch: info.MatchSwitch, SignupCostCoin: info.SignupCostCoin, SignupCostDiamond: info.SignupCostDiamond, MatchTimeType: info.MatchTimeType, MatchTimeStartHMS: info.MatchTimeStartHMS, MatchTimeEndHMS: info.MatchTimeEndHMS, TitleURL: info.TitleURL, AwardShow: info.AwardShow, Rule: info.Rule, SortId: info.SortId, OnChannelName: info.OnChannelName, ShowId: info.ShowId, AwardNum: awardNum, } if info.MatchTimeWeek != nil && len(info.MatchTimeWeek) > 0 { for _, week := range info.MatchTimeWeek { tMInfo.MatchTimeWeek = append(tMInfo.MatchTimeWeek, week) } } if info.MatchType == MatchTypeChampion { week := this.getWeekDay() for _, v := range info.MatchTimeWeek { if v == int32(week) { if info.MatchTimeStamp != nil && len(info.MatchTimeStamp) > 0 { for _, stamp := range info.MatchTimeStamp { tMInfo.MatchTimeStamp = append(tMInfo.MatchTimeStamp, stamp) } } } } if len(tMInfo.MatchTimeStamp) == 0 { tMInfo.MatchTimeStamp = []int64{time.Now().AddDate(0, 0, 2).Unix(), time.Now().AddDate(0, 0, 3).Unix()} } } else { if info.MatchTimeStamp != nil && len(info.MatchTimeStamp) > 0 { for _, stamp := range info.MatchTimeStamp { tMInfo.MatchTimeStamp = append(tMInfo.MatchTimeStamp, stamp) } } } if info.MatchPromotion != nil { for _, mp := range info.MatchPromotion { tMInfo.MatchPromotion = append(tMInfo.MatchPromotion, mp) } } if info.Award != nil { for _, award := range info.Award { miAward := &tournament.MatchInfoAward{ Coin: award.Coin, Diamond: award.Diamond, UpLimit: award.UpLimit, DownLimit: award.DownLimit, } if award.ItemId != nil { for _, itemInfo := range award.ItemId { a := &tournament.ItemInfo{ ItemId: itemInfo.ItemId, ItemNum: int32(itemInfo.ItemNum), Name: itemInfo.Name, } miAward.ItemInfo = append(miAward.ItemInfo, a) } } tMInfo.Award = append(tMInfo.Award, miAward) } } if info.SignupCostItem != nil && info.SignupCostItem.ItemNum > 0 { signupCost := &tournament.ItemInfo{ ItemId: info.SignupCostItem.ItemId, ItemNum: int32(info.SignupCostItem.ItemNum), Name: info.SignupCostItem.Name, } tMInfo.SignupCostItem = signupCost } pack.TMInfo = append(pack.TMInfo, tMInfo) } } if l := this.TypeList[platform]; l != nil { for _, v := range l.GetList() { pack.TypeList = append(pack.TypeList, &tournament.MatchTypeInfo{ Name: v.GetName(), SortId: v.GetSortId(), On: v.GetOn(), Id: v.GetId(), }) } } } return pack } // GetTmMatch 查询比赛中的比赛 func (this *Tournament) GetTmMatch(plt string, matchId int32, channelName string, audience bool, sortId int64) []*TmMatch { matches := this.GetAllMatchInfo(plt) if matches == nil { return nil } var ids []int32 for id, info := range matches { if info == nil || info.MatchSwitch != 1 { continue } if matchId > 0 && id != matchId { continue } if channelName != "" && !common.InMatchChannel(info.OnChannelName, channelName) { continue } if info.GetAudienceSwitch() == 1 != audience { continue } ids = append(ids, id) } var ret []*TmMatch if sortId > 0 { for _, v := range ids { d := this.matches[v] if d != nil { r, ok := d[sortId] if ok && r != nil { return []*TmMatch{r} } } } return ret } for _, v := range ids { for _, vv := range this.matches[v] { ret = append(ret, vv) } } return ret } func (this *Tournament) GetTmRoom(plt string, matchId int32, channelName string, audience bool, sortId int64) []*Scene { tm := this.GetTmMatch(plt, matchId, channelName, audience, sortId) sort.Slice(tm, func(i, j int) bool { return tm[i].SortId < tm[j].SortId }) var ret []*Scene for _, v := range tm { d := SceneMgrSingleton.GetMatchRoom(v.SortId) sort.Slice(d, func(i, j int) bool { return d[i].createTime.Before(d[j].createTime) }) ret = append(ret, d...) } return ret } func (this *Tournament) MakeMatchLog(platform string, tmId int32, sortId int64) *model.MatchLog { gameMatchDate := this.GetMatchInfo(platform, tmId, sortId) if gameMatchDate == nil { logger.Logger.Errorf("MakeMatchLog gameMatchDate == nil tmId:%d sortId:%d", tmId, sortId) return nil } matchLog := model.NewMatchLog() _, ok := this.roundPlayers[sortId] if !ok { return nil } info := this.roundPlayers[sortId][1] if info == nil { return nil } for _, v := range info.players { if v == nil || v.p == nil || v.p.IsRob { continue } var coin, diamond int64 items := make(map[int32]int64) rankId := int32(-1) perRankInfo := this.finalPerRank[sortId] for _, info := range perRankInfo { if info.SnId == v.p.SnId { rankId = info.RankId break } } if gameMatchDate.Award != nil { for _, award := range gameMatchDate.Award { if rankId >= award.UpLimit && rankId <= award.DownLimit { //上下限是反的,我也是醉了 coin = award.Coin diamond = award.Diamond if award.ItemId != nil { for _, info := range award.ItemId { items[info.ItemId] = info.ItemNum } } } } } matchLog.Players = append(matchLog.Players, &model.MatchPlayer{ SnId: v.p.SnId, CostCoin: this.matches[tmId][sortId].gmd.SignupCostCoin, CostDiamond: this.matches[tmId][sortId].gmd.SignupCostDiamond, Coin: coin, Diamond: diamond, Item: items, Rank: rankId, }) if rankId >= 1 && rankId <= 10 { TaskSubjectSingleton.Touch(common.TaskTypeMatchRank10, &TaskData{ GameID: int(v.tm.dbGameFree.GetGameId()), GameFreeID: v.tm.dbGameFree.GetId(), SnId: v.p.SnId, Num: 1, }) } } matchLog.MatchId = tmId matchLog.MatchName = gameMatchDate.MatchName matchLog.Platform = platform matchLog.GameFreeId = gameMatchDate.GameFreeId matchLog.StartTime = time.Unix(this.matches[tmId][sortId].StartTime, 0) matchLog.EndTime = time.Now() matchLog.SortId = sortId return matchLog } func (this *Tournament) saveMatchLog(matchLog *model.MatchLog) { if matchLog == nil { return } task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { err := model.InsertMatchLogs(matchLog) if err != nil { logger.Logger.Error("saveMatchLog error %v", err) return err } return nil }), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) { })).StartByFixExecutor("saveMatchLogTask") } func (this *Tournament) ModuleName() string { return "Tournament" } func (this *Tournament) Init() { logger.Logger.Trace("Tournament Init") for _, v := range PlatformMgrSingleton.GetPlatforms() { if v == nil || v.IdStr == "0" { continue } ret, err := model.GetMatchAward(v.IdStr) if err != nil { logger.Logger.Warnf("GetMatchAward error %v", err) continue } if this.MatchAwardNum == nil { this.MatchAwardNum = make(map[string]map[int32]int32) } this.MatchAwardNum[v.IdStr] = ret.Award } } func (this *Tournament) Update() { // 使用机器人的情况 for snId, info := range this.singleSignupPlayers { if info == nil || info.Ts <= 0 { continue } matchInfo := this.GetMatchInfo(info.Platform, info.TmId, 0) if this.IsMatchOn(matchInfo) && !this.IsOutTime(matchInfo) && this.IsRobotOn(matchInfo) { needTime := this.playerWaitStart[snId] if time.Now().Unix()-info.Ts > needTime { signInfo := &SignInfo{ signup: make(map[int32]*TmPlayer), Platform: info.Platform, MaxCnt: int(matchInfo.MatchNumebr), } seq := rand.Intn(int(matchInfo.MatchNumebr)) + 1 signInfo.signup[snId] = &TmPlayer{SnId: snId, seq: seq} this.signupPlayers[info.Platform][info.TmId] = signInfo //倒计时结束 开始比赛 this.Start(info.Platform, info.TmId) } } else { this.CancelSignUpAll(info.Platform, info.TmId) } } // 防止内存泄露 now := time.Now().Unix() for _, v := range this.matches { for _, vv := range v { if vv != nil && now-vv.StartTime > int64(time.Hour.Seconds()*5) { for _, p := range vv.TmPlayer { this.ForceQuit(vv.Platform, p.SnId) break } } } } } func (this *Tournament) OnDayTimer() { for k := range this.MatchAwardNum { this.MatchAwardNum[k] = make(map[int32]int32) } task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { this.SaveMatchAward() return nil }), nil, "save_match_award_times").Start() } func (this *Tournament) Shutdown() { this.SaveMatchAward() module.UnregisteModule(this) } func (this *Tournament) SaveMatchAward() { if this.MatchAwardNum == nil { return } logger.Logger.Tracef("保存比赛场奖励领取次数") for platform, v := range this.MatchAwardNum { d := &model.MatchAward{ Platform: platform, Award: make(map[int32]int32), } for k, vv := range v { d.Award[k] = vv } err := model.UpsertMatchAward(d) if err != nil { logger.Logger.Errorf("SaveMatchAward error %v", err) } } } func (this *Tournament) getWeekDay() int { getWeekNum := func(t time.Time) int { strWeek := []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} week := t.Weekday() for i, s := range strWeek { if week.String() == s { return i } } return 0 } l := time.FixedZone("CST", model.GameParamData.BackendTimeLocal*3600) bTs := time.Now().In(l) week := getWeekNum(bTs) return week } func (this *Tournament) FixMatchTimeStamp(d *webapiproto.GameMatchDate) { // 更新比赛时间 if d == nil || d.MatchTimeType != 1 { return } if len(d.MatchTimeStamp) != 2 { return } l := time.FixedZone("CST", model.GameParamData.BackendTimeLocal*3600) bTs := time.Now().In(l) week := this.getWeekDay() st := time.Unix(d.MatchTimeStamp[0], 0).In(l) et := time.Unix(d.MatchTimeStamp[1], 0).In(l) //logger.Logger.Tracef("FixMatchTimeStamp 1 id:%v now:%v week:%v start:%v end:%v", d.Id, bTs, bTs.Weekday(), st, et) // 重复时间段比赛时间 for _, v := range d.MatchTimeWeek { if v == int32(week) { stSub := st.Unix() - nowtool.New(st).BeginningOfDay().Unix() etSub := et.Unix() - nowtool.New(et).BeginningOfDay().Unix() sub := etSub - stSub d.MatchTimeStamp[0] = nowtool.New(bTs).BeginningOfDay().Add(time.Duration(stSub) * time.Second).Unix() d.MatchTimeStamp[1] = d.MatchTimeStamp[0] + sub st = time.Unix(d.MatchTimeStamp[0], 0).In(l) et = time.Unix(d.MatchTimeStamp[1], 0).In(l) //logger.Logger.Tracef("FixMatchTimeStamp 2 id:%v now:%v week:%v start:%v end:%v", d.Id, bTs, bTs.Weekday(), st, et) break } } } func (this *Tournament) OnHourTimer() { for _, v := range this.GameMatchDateList { for _, vv := range v { this.FixMatchTimeStamp(vv) } } } // GetMatchAwardNum 剩余奖励数量 // id 比赛配置id func (this *Tournament) GetMatchAwardNum(platform string, id int32) int32 { var num int32 if this.MatchAwardNum != nil && this.MatchAwardNum[platform] != nil { num = this.MatchAwardNum[platform][id] } matchInfo := this.GetAllMatchInfo(platform) if matchInfo != nil { for _, info := range matchInfo { if info.Id == id { if info.AwardNum == 9999 { return 9999 } else { num = info.AwardNum - num if num < 0 { num = 0 } } break } } } return num } func (this *Tournament) GetRound(sortId int64) int32 { d, ok := this.roundPlayers[sortId] if !ok || d == nil { return 1 } return int32(len(d)) }