game_sync/worldsrv/tournament.go

2045 lines
57 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {
_, ok := v.signup[snId]
return ok, 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.AddItem(p, int64(item.ItemId), -gmd.SignupCostItem.ItemNum, 0, common.GainWay_MatchSignup,
"player", gmd.MatchName+"-报名消耗", gameId, int64(gmd.GameFreeId), false)
}
} else {
BagMgrSingleton.AddItem(p, int64(item.ItemId), gmd.SignupCostItem.ItemNum, 0, common.GainWay_MatchSignup,
"player", gmd.MatchName+"-报名退还", gameId, int64(gmd.GameFreeId), false)
}
} 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
}
item := &Item{
ItemId: info.ItemId,
ItemNum: int64(info.ItemNum),
}
BagMgrSingleton.AddItems(mc.p, []*Item{item}, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励", 0, 0, false)
}
}
}
}
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))
}