package main import ( "fmt" "math/rand" "sort" "strconv" "sync" "time" "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" webapi_proto "mongo.games.com/game/protocol/webapi" "mongo.games.com/game/srvdata" "mongo.games.com/game/webapi" ) const ( MatchTypeNormal = 1 // 锦标赛 MatchTypeChampion = 2 // 冠军赛 MatchTypeVIP = 3 // vip比赛 ) const ( MatchSwitchStart = 1 // 开启 MatchSwitchClose = 2 // 关闭 ) const ( MatchUseRobot = 0 // 使用机器人 MatchNotUserRobot = 1 // 关闭机器人 ) // tournament var TournamentMgr = &Tournament{ GameMatchDateList: make(map[string]map[int32]*webapi_proto.GameMatchDate), //比赛配置 matches: make(map[int32]map[int32]*TmMatch), signUpPlayers: make(map[string]map[int32]*SignInfo), players: make(map[int32]map[int32]*PlayerMatchContext), copyPlayers: make(map[int32]map[int32][]*PlayerMatchContext), rankPlayers: make(map[int32]map[int32][]*PlayerMatchContext), roundOverPlayerNum: make(map[int32]map[int32]int32), finalPerRank: make(map[int32][]*PerRankInfo), playerSignUpTime: make(map[int32]int64), playerWaitStart: make(map[int32]int64), singleSignupPlayers: make(map[int32]*SignupInfo), singleFinalGrades: make(map[int32]map[int32]int32), } type Tournament struct { GameMatchDateList map[string]map[int32]*webapi_proto.GameMatchDate // 比赛配置,platform:比赛场配置id:比赛配置 matches map[int32]map[int32]*TmMatch // 开始比赛的数据,比赛配置Id:比赛顺序序号:一场开始的比赛数据 signUpPlayers map[string]map[int32]*SignInfo // 报名的玩家 platform:比赛配置id:报名人 players map[int32]map[int32]*PlayerMatchContext // 比赛中玩家 比赛顺序序号:snid:玩家信息 copyPlayers map[int32]map[int32][]*PlayerMatchContext // 比赛玩家数据备份 比赛顺序序号:round:玩家信息 rankPlayers map[int32]map[int32][]*PlayerMatchContext //比赛玩家为了每轮积分排名用 比赛顺序序号,round roundOverPlayerNum map[int32]map[int32]int32 // 完成比赛人数 比赛序号:轮次:人数 finalPerRank map[int32][]*PerRankInfo //本场比赛最后排名,每淘汰一位记录一位,最后记录决赛玩家 playerWaitStart map[int32]int64 // 等待时间 玩家Id:等待时长秒 singleFinalGrades map[int32]map[int32]int32 //使用机器人模式最后一轮分数 // UseRobot playerSignUpTime map[int32]int64 //玩家报名时间 玩家Id:报名时间戳 singleSignupPlayers map[int32]*SignupInfo //报名的玩家,玩家Id:报名信息 // UseRobot } 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 } // 检查下数据是否合法(主要检查报名人数和晋级方式) n n|...|4|1 func (this *Tournament) checkData(cfg *webapi_proto.GameMatchDate) bool { if cfg == nil { return false } l := len(cfg.MatchPromotion) if l < 2 { return false } if cfg.MatchPromotion[0] != cfg.MatchNumebr { //第一位必须和报名人数相同 return false } if cfg.MatchPromotion[l-1] != 1 { //最后一位必须是1 return false } if cfg.MatchPromotion[l-2] != 4 { //倒数第二位必须是4 return false } 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 } // UpdateData 更新配置 // init: 通知客户端配置变更 func (this *Tournament) UpdateData(init bool, cfgs *webapi_proto.GameMatchDateList) { //logger.Logger.Trace("(this *Tournament) UpdateData:", cfgs) if cfgs.Platform == "0" { return } gmds := make(map[int32]*webapi_proto.GameMatchDate) for _, v := range cfgs.List { if this.checkData(v) { gmds[v.Id] = v } else { logger.Logger.Error("GameMatchDate error: ", v) } } oldgmds := this.GameMatchDateList[cfgs.Platform] //旧配置关闭踢人 --旧配置关闭不影响已经开始的只取消报名未开始的 for _, v := range oldgmds { gmd := gmds[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.signUpPlayers[cfgs.Platform][v.Id] if signInfo != nil && len(signInfo.signup) > 0 { // 取消报名 this.CancelSignUpAll(cfgs.Platform, v.Id) } } } //新配置增加更新 for _, v := range gmds { oldgmd := oldgmds[v.Id] if oldgmd == nil || v.MatchSwitch == MatchSwitchStart { if this.signUpPlayers[cfgs.Platform] == nil { this.signUpPlayers[cfgs.Platform] = make(map[int32]*SignInfo) } this.signUpPlayers[cfgs.Platform][v.Id] = &SignInfo{ signup: make(map[int32]*TmPlayer), Platform: cfgs.Platform, MaxCnt: int(v.MatchNumebr), } } if v.MatchSwitch == MatchSwitchClose || !this.IsTimeRange(v) { this.CancelSignUpAll(cfgs.Platform, v.Id) if v.MatchSwitch == MatchSwitchClose { delete(this.signUpPlayers[cfgs.Platform], v.Id) } } } this.GameMatchDateList[cfgs.Platform] = gmds if !init { //todo 优化 for _, v := range PlayerMgrSington.playerOfPlatform[cfgs.Platform] { //通知平台玩家数据更新 pack := TournamentMgr.GetSCTMInfosPack(cfgs.Platform, v.AppChannel) proto.SetDefaults(pack) logger.Logger.Trace("SCTMInfos++++++++++++:", pack) v.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMInfos), pack) } } } // GetMatchInfo 比赛配置 func (this *Tournament) GetMatchInfo(platform string, tmId int32) *webapi_proto.GameMatchDate { 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 { info := this.GetMatchInfo(platform, tmId) return info != nil && info.MatchSwitch == MatchSwitchStart } // IsUseRobot 是否用机器人 func (this *Tournament) IsUseRobot(platform string, tmId int32) bool { info := this.GetMatchInfo(platform, tmId) return info != nil && info.UseRobot == MatchUseRobot } // 周几 func getWeekNum(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 } // IsTimeRange 判断是否在比赛时间段内 // 在时间段内才能报名 func (this *Tournament) IsTimeRange(gmd *webapi_proto.GameMatchDate) bool { if gmd != nil { if gmd.MatchType == 2 { //冠军赛 tNow := time.Now() switch gmd.MatchTimeType { // 0无时效 1重复时间段 2一次性时间段 case 0: return true case 1: nowWeek := getWeekNum(tNow) hms := int32(tNow.Hour()*10000 + tNow.Minute()*100 + tNow.Second()) if gmd.MatchTimeWeek != nil && len(gmd.MatchTimeWeek) > 0 { for _, week := range gmd.MatchTimeWeek { if nowWeek == int(week) { if hms >= gmd.MatchTimeStartHMS && hms <= gmd.MatchTimeEndHMS { return true } } } } 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 } } } } else { //锦标赛没有时间限制 return true } } return false } // IsOutTime 是否过期 func (this *Tournament) IsOutTime(gmd *webapi_proto.GameMatchDate) bool { if gmd == nil || gmd.MatchSwitch != MatchSwitchStart { 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]*webapi_proto.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) { // 未使用机器人 if this.signUpPlayers != nil && this.signUpPlayers[platform] != nil { for tmid, info := range this.signUpPlayers[platform] { if info.signup != nil { for sid, _ := range info.signup { if sid == snid { return true, tmid } } } } } // 使用机器人了 if v, ok := this.singleSignupPlayers[snid]; ok { return true, v.Tmid } return false, 0 } // Quit 如果正在等待比赛,退赛 func (this *Tournament) Quit(platform string, snid int32) { isWaiting, tmid := this.IsMatchWaiting(platform, snid) if isWaiting { this.CancelSignUp(platform, tmid, snid) } } // signUpCost 报名 // tmId 比赛配置id // cost true报名、false取消报名 // 报名费用 0成功 3道具不足 5金币不足 6钻石不足 7免费次数不足 func (this *Tournament) signUpCost(tmId int32, p *Player, cost bool) (bool, int32) { logger.Logger.Trace("报名费用 signUpCost: ", tmId, " snid: ", p.SnId, " cost: ", cost) if p == nil { return false, int32(tournament.SignRaceCode_OPRC_NoItem) } gmd := this.GetMatchInfo(p.Platform, tmId) if gmd != nil && !p.IsRob { //真人费用检测 if gmd.MatchType == MatchTypeVIP { //VIP比赛场 freeTimes := p.GetVIPPrivilege1() // VIP比赛场免费次数 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.Trace("比赛场报名消耗金币", 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.Trace("比赛场报名消耗钻石", 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.Trace("比赛场报名消耗道具", cost, gmd.SignupCostItem.ItemNum) item := BagMgrSingleton.GetItem(p.SnId, gmd.SignupCostItem.ItemId) if item != nil { if cost { if item.ItemNum < gmd.SignupCostItem.ItemNum { logger.Logger.Trace("道具不足") return false, int32(tournament.SignRaceCode_OPRC_NoItem) } else { BagMgrSingleton.SalePlayerItem(p, item, gmd.SignupCostItem.ItemNum) //item.ItemNum -= gmd.SignupCostItem.ItemNum //p.dirty = true BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, gmd.SignupCostItem.ItemNum, gmd.MatchName+"-报名消耗") //BagMgrSingleton.SyncBagData(p, item.ItemId) } } else { BagMgrSingleton.SalePlayerItem(p, item, -gmd.SignupCostItem.ItemNum) //item.ItemNum += gmd.SignupCostItem.ItemNum //p.dirty = true BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, item.ItemId, item.Name, gmd.SignupCostItem.ItemNum, gmd.MatchName+"-报名退还") //BagMgrSingleton.SyncBagData(p, item.ItemId) } } 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.Trace("(this *Tournament) SignUp:", tmId, p.SnId) // 开启 info := this.GetMatchInfo(p.Platform, tmId) if info == nil || info.GetMatchSwitch() == MatchSwitchClose { return false, int32(tournament.SignRaceCode_OPRC_Close) } // 报名时间 if !TournamentMgr.IsTimeRange(info) { return false, int32(tournament.SignRaceCode_OPRC_Time) } // 已报名 isWaiting, _ := TournamentMgr.IsMatchWaiting(p.Platform, p.SnId) if TournamentMgr.IsMatching(p.SnId) || isWaiting { return false, int32(tournament.SignRaceCode_OPRC_Repeat) } // 扣报名费 ok, code := this.signUpCost(tmId, p, true) if !ok { return false, code } var signInfo *SignInfo gmd := this.GetMatchInfo(p.Platform, tmId) if gmd == nil { logger.Logger.Errorf("GetMatchInfo is nil. id=%v", tmId) return false, int32(tournament.SignRaceCode_OPRC_Repeat) } if this.IsUseRobot(p.Platform, tmId) { signupInfo := &SignupInfo{ Platform: p.Platform, Tmid: tmId, Snid: p.SnId, } this.singleSignupPlayers[p.SnId] = signupInfo this.playerSignUpTime[p.SnId] = time.Now().Unix() } else { signInfo = &SignInfo{ signup: make(map[int32]*TmPlayer), Platform: p.Platform, MaxCnt: int(gmd.MatchNumebr), } var platform = p.Platform gps := PlatformMgrSingleton.GetGameFree(p.Platform, gmd.GameFreeId) if gps != nil && gps.GroupId != 0 { for plt := range this.signUpPlayers { gps2 := PlatformMgrSingleton.GetGameFree(plt, gmd.GameFreeId) if gps.GroupId == gps2.GroupId { platform = plt break } } } if _, ok := this.signUpPlayers[platform][tmId]; !ok { this.signUpPlayers[platform][tmId] = signInfo } else { signInfo = this.signUpPlayers[platform][tmId] } if _, ok := signInfo.signup[p.SnId]; !ok { n := len(signInfo.signup) + 1 signInfo.signup[p.SnId] = &TmPlayer{SnId: p.SnId, seq: n} logger.Logger.Trace("没使用机器人,真人 报名.............", n, " p.SnId: ", p.SnId) } else { return false, int32(tournament.SignRaceCode_OPRC_Repeat) } } //(报名人数/20,报名人数/10) minTime := gmd.MatchNumebr / 20 maxTime := gmd.MatchNumebr / 10 if gmd.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(tmid, p, false) delete(this.signUpPlayers[platform][tmid].signup, snid) //通知取消报名 pack := &tournament.SCSignRace{ OpCode: 1, RetCode: 0, } proto.SetDefaults(pack) logger.Logger.Trace("signUpPlayers: ", pack) p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCSignRace), pack) return } } } if signupinfo, ok := this.singleSignupPlayers[snid]; ok { p := PlayerMgrSington.GetPlayerBySnId(snid) if p != nil && p.scene == nil { //退费 this.signUpCost(signupinfo.Tmid, p, false) delete(this.singleSignupPlayers, snid) delete(this.playerSignUpTime, snid) //通知取消报名 pack := &tournament.SCSignRace{ OpCode: 1, RetCode: 0, } proto.SetDefaults(pack) logger.Logger.Trace("singleSignupPlayers: ", pack) p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_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) } } } } // 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) 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) } matchInfo := this.GetMatchInfo(platform, tmId) signInfo := this.signUpPlayers[platform][tmId] sortId := int32(time.Now().Nanosecond()) tm := &TmMatch{ TMId: tmId, SortId: sortId, gmd: matchInfo, Platform: signInfo.Platform, tmpUseRobot: matchInfo.UseRobot, } tm.gml = srvdata.MatchLevelMgr.Get(matchInfo.GameFreeId, matchInfo.MatchLevel) tm.dbGameFree = srvdata.PBDB_GameFreeMgr.GetData(matchInfo.GameFreeId) tm.CopyMap(signInfo.signup) if this.matches[tmId] == nil { this.matches[tmId] = make(map[int32]*TmMatch) } logger.Logger.Tracef("开赛:%+v, 难度:%v", tm, tm.gml) // 开始比赛,清除报名数据 this.matches[tmId][sortId] = tm tm.Start() this.signUpPlayers[platform][tmId].signup = make(map[int32]*TmPlayer) } // NextRoundStartSingle 下一轮是否开始 func (this *Tournament) NextRoundStartSingle(sortId int32, mtp *PlayerMatchContext, matchRobotGrades map[int32]int32) { logger.Logger.Tracef("================NextRoundStart_Single==========当前第 %v 轮============", mtp.round) if _, ok := this.copyPlayers[sortId]; ok { if _, ok1 := this.copyPlayers[sortId][mtp.round]; ok1 { gmd := mtp.tm.gmd //需要晋级的人数 promotionNum := gmd.MatchPromotion[mtp.round] // 晋级人数 if promotionNum != 1 { // 非决赛 if mtp.tm.robotGrades == nil || mtp.tm.robotGrades[int(mtp.round)] == nil { mtp.tm.CreateRobotGrades(int(mtp.round)) } if mtp.tm.robotGrades != nil && mtp.tm.robotGrades[int(mtp.round)] != nil { robotGrades := mtp.tm.robotGrades[int(mtp.round)] // 修改陪真人玩的机器人积分 if matchRobotGrades != nil { logger.Logger.Trace("matchRobotGrades: ", matchRobotGrades) for _, gradeInfo := range robotGrades { if grade, ok := matchRobotGrades[gradeInfo.copySnid]; ok { gradeInfo.grade = grade } } } //非决赛淘汰后开始配桌 logger.Logger.Trace("需要晋级的人数 promotionNum: ", promotionNum) logger.Logger.Trace("非决赛开始淘汰晋级 grade: ", mtp.grade) // 记录真人积分 findMe := false for _, gradeinfo := range robotGrades { if gradeinfo.copySnid == 0 { gradeinfo.grade = mtp.grade findMe = true break } } if !findMe { gradeInfo := &TmGradeInfo{ grade: mtp.grade, copySnid: 0, } robotGrades = append(robotGrades, gradeInfo) // 添加了真人数据 } sort.Slice(robotGrades, func(i, j int) bool { return robotGrades[i].grade > robotGrades[j].grade }) for _, info := range robotGrades { logger.Logger.Trace("Snid: ", info.copySnid, " grade: ", info.grade, " copyLv: ", info.copyLv, " copyRoleId: ", info.copyRoleId) } // 获取排名 index := int32(0) //晋级淘汰 for i, gradeinfo := range robotGrades { if mtp.grade >= gradeinfo.grade { index = int32(i) + 1 break } } robotGrades = append(robotGrades[:promotionNum]) mtp.tm.robotGrades[int(mtp.round)] = robotGrades if index == 0 { // 是最后一名 index = gmd.MatchPromotion[mtp.round-1] } if index <= promotionNum { mtp.rank = index this.sendPromotionInfo(mtp, sortId, JinJi, false, false) //晋级 mct := []*PlayerMatchContext{mtp} finals := false if mtp.round == int32(len(gmd.MatchPromotion)-2) { finals = true } timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { MatchSceneMgrSingleton.NewRoundStart(mtp.tm, mct, finals, mtp.round+1) return true }), nil, time.Second*7, 1) } else { mtp.rank = index pri := &PerRankInfo{ Name: mtp.p.Name, SnId: mtp.p.SnId, RankId: mtp.rank, Grade: mtp.grade, } this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) this.sendPromotionInfo(mtp, sortId, TaoTai, true, false) //淘汰 } } } else { // 比赛结束 // 修改陪真人玩的机器人积分 robotGrades := mtp.tm.robotGrades[int(mtp.round-1)] if matchRobotGrades != nil { logger.Logger.Trace("matchRobotGrades: ", matchRobotGrades) for snid, grade := range matchRobotGrades { logger.Logger.Trace("Snid: ", snid, " grade: ", grade) } for _, gradeInfo := range robotGrades { if grade, ok := matchRobotGrades[gradeInfo.copySnid]; ok { gradeInfo.grade = grade } } } // 记录真人积分 for _, gradeinfo := range robotGrades { if gradeinfo.copySnid == 0 { gradeinfo.grade = mtp.grade break } } sort.Slice(robotGrades, func(i, j int) bool { return robotGrades[i].grade > robotGrades[j].grade }) logger.Logger.Trace("robotGrades: ") for _, info := range robotGrades { logger.Logger.Trace("Snid: ", info.copySnid, " grade: ", info.grade, " copyLv: ", info.copyLv, " copyRoleId: ", info.copyRoleId) } // 获取真人名次 index := int32(0) //晋级淘汰 for i, gradeinfo := range robotGrades { if mtp.grade >= gradeinfo.grade { index = int32(i) + 1 break } } if index == 0 { index = gmd.MatchPromotion[mtp.round-1] //最后一名 } mtp.rank = index if this.finalPerRank[sortId] == nil { this.finalPerRank[sortId] = []*PerRankInfo{} } pri := &PerRankInfo{ Name: mtp.p.Name, SnId: mtp.p.SnId, RankId: mtp.rank, Grade: mtp.grade, } this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) //把决赛的玩家记录在排行榜 outCode := TaoTai if mtp.rank == 1 { outCode = JinJi } this.sendPromotionInfo(mtp, sortId, outCode, true, true) //晋级 logger.Logger.Trace("比赛结束!!! ") // 比赛结束 this.StopMatch(mtp.tm.TMId, sortId) } } } } // 下一轮是否开始 func (this *Tournament) NextRoundStart(sortId int32, mtp *PlayerMatchContext) { logger.Logger.Tracef("================NextRoundStart==========当前第 %v 轮============", mtp.round) if _, ok := this.copyPlayers[sortId]; ok { if mcs, ok1 := this.copyPlayers[sortId][mtp.round]; ok1 { gmd := mtp.tm.gmd //需要晋级的人数 promotionNum1 := gmd.MatchPromotion[mtp.round-1] promotionNum := gmd.MatchPromotion[mtp.round] //通知晋级信息:0.晋级等待匹配 1.失败退出 2.等待判断是否晋级 pack := &tournament.SCPromotionInfo{} if promotionNum != 1 { n := int32(len(mcs)) //非决赛淘汰后开始配桌 logger.Logger.Trace("非决赛开始淘汰晋级") outNum := promotionNum1 - promotionNum //已经晋级的人数减去一桌之后 剩余人数还能够满足本轮淘汰 logger.Logger.Trace("n: ", n, " outNum: ", outNum) if n-4 >= outNum { //提前晋级的开始凑桌 MatchContextSlice(mcs).Sort(false) //挑选出晋级的玩家 meIn := false //自己晋级 for k, v := range mcs { if mtp.p.SnId == v.p.SnId { meIn = true } logger.Logger.Trace("排序之后=========== ", k, v.p.SnId, v.round, v.seq, v.grade) } mct := []*PlayerMatchContext{} finals := false for i := 0; i < len(mcs)-int(outNum); i++ { var mc PlayerMatchContext mc = *mcs[i] this.sendPromotionInfo(mcs[i], sortId, JinJi, false, false) //晋级 mc.rank = mcs[i].rank mct = append(mct, &mc) logger.Logger.Trace("======凑桌==========mc=================", mc) if !finals && mc.round == int32(len(gmd.MatchPromotion)-2) { finals = true } } mcs = mcs[len(mct):] this.copyPlayers[sortId][mtp.round] = this.copyPlayers[sortId][mtp.round][len(mct):] willOut := false if promotionNum1 == this.roundOverPlayerNum[sortId][mtp.round-1] { //最后一个人打完了,确定要淘汰的人 willOut = true } else { if !meIn { //自己暂时没晋级 this.sendPromotionInfo(mtp, sortId, DaiDing, false, false) //待定 } } if this.finalPerRank[sortId] == nil { this.finalPerRank[sortId] = []*PerRankInfo{} } isOver := false for k, v := range this.copyPlayers[sortId][mtp.round] { logger.Logger.Trace("凑桌之后剩余===2======== ", k, v.p.SnId, v.round, v.seq, v.grade) if willOut { this.sendPromotionInfo(v, sortId, TaoTai, true, false) //淘汰 //把淘汰的玩家记录在排行榜 pri := &PerRankInfo{ Name: v.p.Name, SnId: v.p.SnId, RankId: pack.RankId, Grade: v.grade, } this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) //真人被淘汰,如果剩下的都是机器人,比赛解散 if !v.p.IsRob { 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(mtp.tm.TMId, sortId) } } } } } if !isOver { timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { MatchSceneMgrSingleton.NewRoundStart(mtp.tm, mct, finals, mtp.round+1) return true }), nil, time.Second*7, 1) } } else { this.sendPromotionInfo(mtp, sortId, DaiDing, false, false) //待定 } } else { if len(mcs) == 4 { MatchContextSlice(mcs).Sort(true) if this.finalPerRank[sortId] == nil { this.finalPerRank[sortId] = []*PerRankInfo{} } for _, mc := range mcs { pri := &PerRankInfo{ Name: mc.p.Name, SnId: mc.p.SnId, RankId: mc.rank, Grade: mc.grade, } this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) //把决赛的玩家记录在排行榜 outCode := TaoTai if mc.rank == 1 { outCode = JinJi } this.sendPromotionInfo(mc, sortId, outCode, true, true) //晋级 } logger.Logger.Trace("比赛结束!!! ") // 比赛结束 this.StopMatch(mtp.tm.TMId, sortId) } } } } } const ( JinJi = 0 // 晋级 TaoTai = 1 // 淘汰 DaiDing = 2 // 待定 ) // 发送晋级信息 // outCode 0晋级 1淘汰 2待定 // isOver 玩家比赛结束 // isFinals 比赛结束 func (this *Tournament) sendPromotionInfo(mc *PlayerMatchContext, sortId int32, outCode int, isOver, isFinals bool) { if mc == nil { return } rankId := mc.rank if mc.tm.tmpUseRobot == MatchNotUserRobot { // 不使用机器人 rankId = this.getRank(sortId, mc.round, mc.p.SnId, isFinals) mc.rank = rankId } //通知晋级信息:0.晋级等待匹配 1.失败退出 2.等待判断是否晋级 pack := &tournament.SCPromotionInfo{} pack.RetCode = int32(outCode) pack.Round = mc.round pack.RankId = rankId pack.RoundCoin = 100 //暂时用不到先写死 pack.Record = mc.record if mc.tm != nil { pack.MatchId = mc.tm.TMId } if mc.tm != nil && mc.tm.gmd != nil { pack.MatchPromotion = mc.tm.gmd.GetMatchPromotion() pack.MatchName = mc.tm.gmd.GetMatchName() if !mc.p.IsRob && isOver { //真人发奖 if mc.tm.gmd.Award != nil { for _, award := range mc.tm.gmd.Award { if rankId >= award.UpLimit && rankId <= 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) } } // test //rankAward.ItemInfo = append(rankAward.ItemInfo, &tournament.ItemInfo{ // ItemId: 30003, // ItemNum: 1, // Name: "1元话费兑换码", //}) // test pack.RankAward = rankAward } } } } } sendFunc := func() { outStr := "晋级" switch outCode { case 1: outStr = "淘汰" case 2: outStr = "待定" } proto.SetDefaults(pack) logger.Logger.Trace("sendPromotionInfo: ", outStr, " snid: ", mc.p.SnId, " pack: ", pack) ok := mc.p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCPromotionInfo), 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 } item := &Item{ ItemId: info.ItemId, ItemNum: int64(info.ItemNum), } BagMgrSingleton.AddJybBagInfo(mc.p, []*Item{item}, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励") data := srvdata.PBDB_GameItemMgr.GetData(info.ItemId) if data != nil { // 背包变更记录 BagMgrSingleton.RecordItemLog(mc.p.Platform, mc.p.SnId, ItemObtain, item.ItemId, data.Name, item.ItemNum, mc.tm.gmd.MatchName+"排名奖励") } } } } } if isOver && mc.tm != nil { // 自己比赛结束 if !mc.p.IsRob { this.CheckAddMatchSeasonLv(mc) } delete(this.players[sortId], mc.p.SnId) } } // 获取兑换码奖品 var has bool wg := new(sync.WaitGroup) if pack.GetRankAward() != nil { for _, v := range pack.GetRankAward().GetItemInfo() { data := srvdata.PBDB_GameItemMgr.GetData(v.ItemId) if data != nil && data.GetType() == common.ItemTypePhoneCode { has = true item := v // 并发需要复制 wg.Add(1) // 兑换码奖品 var err error var newMsg *model.Message res := &webapi_proto.SAGetMatchAwardCode{} task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { // 获取兑换码 pack := &webapi_proto.ASGetMatchAwardCode{ Platform: mc.p.Platform, Snid: mc.p.SnId, ItemID: data.GetId(), Money: data.GetNum(), Tel: mc.p.Tel, } 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() != webapi_proto.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) 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() != webapi_proto.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) } }), 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() })).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) } } } // 比赛结束 func (this *Tournament) StopMatch(tmId, sortId int32) { //房间清理 if this.matches[tmId] != nil && this.matches[tmId][sortId] != nil { this.matches[tmId][sortId].Stop() delete(this.matches[tmId], sortId) } //数据清理 delete(this.players, sortId) delete(this.copyPlayers, sortId) delete(this.rankPlayers, sortId) delete(this.roundOverPlayerNum, sortId) delete(this.finalPerRank, sortId) } func (this *Tournament) getRank(sortId, round, snid int32, isFinals bool) int32 { if _, ok := this.rankPlayers[sortId]; ok { if rps, ok1 := this.rankPlayers[sortId][round]; ok1 { MatchContextSlice(rps).Sort(isFinals) for _, rp := range rps { if rp.p.SnId == snid { return rp.rank } } } } return 0 } func (this *Tournament) isOut(sortId, snid int32) bool { out := false if this.finalPerRank[sortId] != nil { for _, info := range this.finalPerRank[sortId] { if info.SnId == snid { out = true } } } return out } func (this *Tournament) GetTm(sortId int32) *TmMatch { if this.matches != nil { for _, sortTms := range this.matches { for id, match := range sortTms { if id == sortId { return match } } } } return nil } // 玩家比赛结束 更新积分 func (this *Tournament) UpdateMatchInfo(p *Player, sortId, grade, isWin int32, matchRobotGrades map[int32]int32) { logger.Logger.Trace("=========== UpdateMatchInfo ==============") logger.Logger.Tracef("UpdateMatchInfo: sortId:%v, grade:%v, isWin: %v, matchRobotGrades:%v", sortId, grade, isWin, matchRobotGrades) if _, ok := this.players[sortId]; !ok { this.players[sortId] = make(map[int32]*PlayerMatchContext) } if mtp, ok := this.players[sortId][p.SnId]; ok { mtp.grade = grade if this.roundOverPlayerNum[sortId] == nil { this.roundOverPlayerNum[sortId] = make(map[int32]int32) } this.roundOverPlayerNum[sortId][mtp.round]++ if mtp.record == nil { mtp.record = make(map[int32]int32) } mtp.record[isWin]++ //轮数增加 mtp.round++ mtp.gaming = false if this.copyPlayers[sortId] == nil { this.copyPlayers[sortId] = make(map[int32][]*PlayerMatchContext) } var mc PlayerMatchContext mc = *mtp this.copyPlayers[sortId][mtp.round] = append(this.copyPlayers[sortId][mtp.round], &mc) if this.rankPlayers[sortId] == nil { this.rankPlayers[sortId] = make(map[int32][]*PlayerMatchContext) } this.rankPlayers[sortId][mtp.round] = append(this.rankPlayers[sortId][mtp.round], &mc) logger.Logger.Tracef("========snid(%v) grade(%v) ============", p.SnId, grade) if mtp.tm.tmpUseRobot == MatchUseRobot { // 使用机器人 this.NextRoundStartSingle(sortId, mtp, matchRobotGrades) } else { this.NextRoundStart(sortId, mtp) } } } func (this *Tournament) CreatePlayerMatchContext(p *Player, m *TmMatch, seq int) *PlayerMatchContext { ms := MatchSeasonMgrSington.GetMatchSeason(p.SnId) var lv int32 if ms != nil { lv = ms.Lv } roleId := int32(2000001) if p.Roles != nil { roleId = p.Roles.ModId } mc := NewMatchContext(p, m, 1000, p.SnId, lv, roleId, 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 } // 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) { 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, } if info.MatchTimeWeek != nil && len(info.MatchTimeWeek) > 0 { for _, week := range info.MatchTimeWeek { tMInfo.MatchTimeWeek = append(tMInfo.MatchTimeWeek, week) } } 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) } } } return pack } func (this *Tournament) ModuleName() string { return "Tournament" } // 初始化 func (this *Tournament) Init() { logger.Logger.Trace("(this *Tournament) Init()") } func (this *Tournament) Update() { // 使用机器人的情况 for snid, info := range this.singleSignupPlayers { if this.playerSignUpTime[snid] != 0 { matchInfo := this.GetMatchInfo(info.Platform, info.Tmid) if matchInfo.MatchSwitch == MatchSwitchStart && !this.IsOutTime(matchInfo) && this.IsUseRobot(info.Platform, info.Tmid) { needTime := this.playerWaitStart[snid] if time.Now().Unix()-this.playerSignUpTime[snid] > needTime { delete(this.singleSignupPlayers, snid) delete(this.playerSignUpTime, snid) 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) } } } } func (this *Tournament) Shutdown() { } func init() { module.RegisteModule(TournamentMgr, time.Second, 0) }