package singlematch import ( "encoding/json" "fmt" "math/rand" "sort" "time" "bet24.com/log" "bet24.com/servers/common" item "bet24.com/servers/micros/item_inventory/proto" "bet24.com/servers/micros/matches/handler/matchbase" pb "bet24.com/servers/micros/matches/proto" notification "bet24.com/servers/micros/notification/proto" privateroom "bet24.com/servers/micros/privateroom/proto" robot "bet24.com/servers/micros/userservices/proto" userservices "bet24.com/servers/micros/userservices/proto" ) // 单个用户实例 type singlematchinstance struct { pb.SingleMatchInfo matchConfig *SingleMatchConfig robotScores []int roomNo int tmpRobotIds []int reviveBeginTime int } func newMatchInstance(userId int, matchConfig *SingleMatchConfig) *singlematchinstance { ret := new(singlematchinstance) ret.MatchId = matchConfig.MatchId ret.UserId = userId ret.matchConfig = matchConfig ret.StartTime = time.Now().Unix() ret.tmpRobotIds = make([]int, matchConfig.TableUser-1) ret.Rank = 1 for _, v := range matchConfig.Rounds { ret.RoundInfo = append(ret.RoundInfo, v.SingleMatchRoundInfo) } return ret } func (si *singlematchinstance) dump() { log.Release(" UserId[%d]MatchId[%d]Round[%d]Score[%d]Rank[%d]", si.UserId, si.MatchId, si.RoundIndex, si.UserScore, si.Rank) } func (si *singlematchinstance) isEnded() bool { return si.EndTime > 0 } func (si *singlematchinstance) startRound() { // 读取配置 round := si.matchConfig.getRound(si.RoundIndex) if round == nil { log.Release("singlematchinstance.startRound[%d] round not found,end", si.RoundIndex) // come to end si.endMatch() return } si.RoundIndex = round.Index if len(si.robotScores) == 0 { // 第一轮,创建虚拟分数 for i := 0; i < round.TotalUser-1; i++ { si.robotScores = append(si.robotScores, si.matchConfig.InitScore) } si.UserScore = si.matchConfig.InitScore } else { // 先删除已淘汰的分数 if round.TotalUser-1 < len(si.robotScores) { si.robotScores = si.robotScores[:round.TotalUser-1] } // 后面轮次,分数衰减 for i := 0; i < len(si.robotScores); i++ { if si.robotScores[i] > 0 { si.robotScores[i] = si.robotScores[i] * (100 - si.matchConfig.ScoreShrinkPercent) / 100 } } if si.UserScore > 0 { si.UserScore = si.UserScore * (100 - si.matchConfig.ScoreShrinkPercent) / 100 } } // 打乱机器人分数 si.shuffleRobotScores() // 计算所有机器人的分数 si.calRobotScores() // 给玩家创建一个房间 si.createPrivateRoom(round) } func (si *singlematchinstance) endMatch() { if si.UserId == 0 { log.Debug("singlematchinstance endMatch UserId cleared") return } // 比赛结束,可能是输也可能是赢 // 发放奖励 prize := si.matchConfig.getFinalPrize(si.Rank) if len(prize) > 0 { item.AddItems(si.UserId, prize, "singlematch prize", common.LOGTYPE_SINGLEMATCH_PRIZE) } if si.Rank == 1 { userservices.DoRecord(si.UserId, userservices.Record_LadderWinCount, 1) } si.postUserRankNotification(prize) // 处理完后,清理用户ID log.Debug("singlematchinstance endMatch clear UserId[%d] robotScores%v", si.UserId, si.robotScores) si.dump() si.UserId = 0 si.EndTime = time.Now().Unix() } func (si *singlematchinstance) shuffleRobotScores() { rand.Shuffle(len(si.robotScores), func(i, j int) { si.robotScores[i], si.robotScores[j] = si.robotScores[j], si.robotScores[i] }) } func (si *singlematchinstance) calRobotScores() { startIndex := si.matchConfig.TableUser - 1 groupCount := (len(si.robotScores) - startIndex) / si.matchConfig.TableUser for i := 0; i < groupCount; i++ { // 随机一个分数 score := si.matchConfig.getRandomScore(si.RoundIndex) si.robotScores[startIndex+i*si.matchConfig.TableUser] += score si.robotScores[startIndex+i*si.matchConfig.TableUser+1] += score si.robotScores[startIndex+i*si.matchConfig.TableUser+2] -= score si.robotScores[startIndex+i*si.matchConfig.TableUser+3] -= score } } func (si *singlematchinstance) createPrivateRoom(round *RoundInfo) { var err string si.roomNo, err = privateroom.CreatePrivateRoomByUser(-1, si.matchConfig.GameId, si.matchConfig.GameRule, -round.SetCount, si.matchConfig.TableUser, 0, 0, privateroom.RoomType_SimpleMatch, si.matchConfig.PlayTime, false, "") if si.roomNo == 0 { log.Release("singlematchinstance.createPrivateRoom failed UserId[%d],err[%s]", si.UserId, err) si.endMatch() return } // 把玩家拉入房间 go si.arrangeUser(round) } func (si *singlematchinstance) arrangeUser(round *RoundInfo) { // 找出机器人 userIds := make([]int, si.matchConfig.TableUser) userScores := make([]int, si.matchConfig.TableUser) for i := 0; i < len(userIds)-1; i++ { userIds[i] = robot.GetARobot() userScores[i] = si.robotScores[i] si.tmpRobotIds[i] = userIds[i] } userIds[si.matchConfig.TableUser-1] = si.UserId userScores[si.matchConfig.TableUser-1] = si.UserScore for k, v := range userIds { usr := userservices.GetUserInfo(v) if usr == nil { log.Release("singlematchinstance.arrangeUser userId[%d] not found", v) continue } var ret struct { ErrMsg string ServerAddr string TableId int ChairId int } retString := privateroom.UserRequestSit(si.roomNo, userIds[k], usr.NickName, usr.FaceId, usr.FaceUrl, k, userScores[k], round.BaseScore, si.RoundIndex) if err := json.Unmarshal([]byte(retString), &ret); err != nil { log.Release("singlematchinstance.arrangeUser try enter privateroom unmarshal failed %v", err) return } if ret.ServerAddr == "" { log.Release("singlematchinstance.arrangeUser try enter privateroom failed %s", ret.ErrMsg) return } log.Debug("singlematchinstance.arrangeUser user[%d] enter room %d chair %d", userIds[k], si.roomNo, ret.ChairId) sec := 60 if k != len(userIds)-1 { sec = 5 } else { go si.postUserMatchRoomNotification(usr.UserId, ret.ServerAddr, ret.TableId, ret.ChairId) } privateroom.ForceUserEnter(userIds[k], si.roomNo, ret.ChairId, sec) } } func (si *singlematchinstance) postUserMatchRoomNotification(userId int, serverAddr string, tableId, chairId int) { // 发送给所有人 ni := matchbase.Match_notificationInfo{Msg: matchbase.Match_noti_matchroom, ServerAddr: serverAddr, TableId: tableId, ChairId: chairId} d, _ := json.Marshal(ni) notification.AddNotification(userId, notification.Notification_Match, string(d)) } func (si *singlematchinstance) postUserRankNotification(prize []item.ItemPack) { ni := matchbase.Match_notificationInfo{Msg: matchbase.Match_noti_rank, UserId: si.UserId, Rank: si.Rank, PrizeItems: prize} d, _ := json.Marshal(ni) notification.AddNotification(si.UserId, notification.Notification_Match, string(d)) } func (si *singlematchinstance) postUserPromotedNotification(prize []item.ItemPack) { matchData, _ := json.Marshal(si) ni := matchbase.Match_notificationInfo{ Msg: matchbase.Match_noti_promoted, PrizeItems: prize, MatchData: string(matchData), } d, _ := json.Marshal(ni) notification.AddNotification(si.UserId, notification.Notification_Match, string(d)) } func (si *singlematchinstance) postUserEliminatedNotification(reviveCost item.ItemPack) { ni := matchbase.Match_notificationInfo{Msg: matchbase.Match_noti_eliminated, ReviveCost: reviveCost, ReviveTimeoutSec: si.matchConfig.ReviveTimeoutSec} d, _ := json.Marshal(ni) notification.AddNotification(si.UserId, notification.Notification_Match, string(d)) } func (si *singlematchinstance) onRoomEnd(roomNo int, winners []int) bool { if roomNo != si.roomNo { return false } log.Debug("singlematchinstance onRoomEnd %d", roomNo) round := si.matchConfig.getRound(si.RoundIndex) if round == nil { log.Release("singlematchinstance.onRoomEnd failed round of index[%d] not found", si.RoundIndex) return true } isWinner := false for i := 0; i < len(winners); i++ { if winners[i] == si.UserId { isWinner = true break } } if isWinner { si.WinCount++ } else { si.LoseCount++ } // 如果是按输赢淘汰 if round.EliminatByLose { if !isWinner { si.onUserEleminated(round) } else { si.onUserPromoted(false) } return true } // 本轮结束了 // 分数已经刷新,获取一下玩家的名次 sort.Slice(si.robotScores, func(i, j int) bool { return si.robotScores[i] > si.robotScores[j] }) // 看下我的名次 var userRank int for i := 0; i < len(si.robotScores); i++ { if si.UserScore >= si.robotScores[i] { userRank = i + 1 break } } if userRank == 0 { userRank = len(si.robotScores) } si.Rank = userRank log.Debug("UserId[%d]Score[%d]Rank[%d]robotScores%v", si.UserId, si.UserScore, userRank, si.robotScores) if si.matchConfig.isFinalRound(si.RoundIndex) { si.endMatch() return true } // 是否被淘汰? if userRank >= round.TotalUser-round.EliminatUser { si.onUserEleminated(round) } else { si.onUserPromoted(false) } return true } func (si *singlematchinstance) onRoomStart(roomNo int) { if roomNo != si.roomNo { return } } // 游戏结束后先同步分数,再调用onRoomEnd func (si *singlematchinstance) onRoomUserScoreChanged(roomNo int, userId int, score int) bool { if roomNo != si.roomNo { return false } log.Debug("singlematchinstance onRoomUserScoreChanged %d userId[%d]score[%d]", roomNo, userId, score) if userId == si.UserId { si.UserScore += score return true } // 机器人 for i := 0; i < len(si.tmpRobotIds); i++ { if userId == si.tmpRobotIds[i] { si.robotScores[i] += score return true } } log.Release("singlematchinstance.onRoomUserScoreChanged roomNo[%d],userId[%d],score[%d] not handled", roomNo, userId, score) return true } func (si *singlematchinstance) onUserEleminated(round *RoundInfo) { if round.ReviveCost.Count == 0 || si.Revived { si.endMatch() return } // 等待玩家购买晋级 time.AfterFunc(time.Second*time.Duration(si.matchConfig.ReviveTimeoutSec), si.onEleminatedTimeout) si.reviveBeginTime = int(time.Now().Unix()) si.postUserEliminatedNotification(round.ReviveCost) si.ReviveCost = round.ReviveCost } func (si *singlematchinstance) onEleminatedTimeout() { si.ReviveCost.ItemId = 0 si.ReviveCost.Count = 0 si.ReviveSecs = 0 // 已经选择复活或者已经结束 if si.Revived || si.UserId == 0 { return } si.endMatch() } func (si *singlematchinstance) onUserPromoted(revive bool) { round := si.matchConfig.getRound(si.RoundIndex + 1) // 是否最后一轮 if si.matchConfig.isFinalRound(si.RoundIndex) || round == nil { si.endMatch() return } si.RoundIndex++ prize := round.Prize if revive { prize = nil } // 发送奖品 if len(prize) > 0 { item.AddItems(si.UserId, prize, "singlematch prize", common.LOGTYPE_SINGLEMATCH_PRIZE) } si.postUserPromotedNotification(prize) time.AfterFunc(10*time.Second, si.startRound) } func (si *singlematchinstance) isTimeout() bool { now := time.Now().Unix() if si.EndTime > 0 { return now-si.EndTime > 600 } if now-si.StartTime > 3600 { log.Release("singlematchinstance isTimeout 异常超时 ") si.dump() return true } return false } func (si *singlematchinstance) noRevive() bool { if si.UserId == 0 || si.EndTime > 0 { log.Release("singlematchinstance.noRevive userId[%d] ended", si.UserId) return false } si.onEleminatedTimeout() return true } func (si *singlematchinstance) revive() bool { if si.UserId == 0 || si.EndTime > 0 { log.Release("singlematchinstance.revive userId[%d] ended", si.UserId) return false } if si.Revived { log.Release("singlematchinstance.revive userId[%d] Revived", si.UserId) return false } si.ReviveCost.ItemId = 0 si.ReviveCost.Count = 0 si.ReviveSecs = 0 round := si.matchConfig.getRound(si.RoundIndex) if round == nil || round.ReviveCost.Count == 0 { log.Release("singlematchinstance.revive userId[%d] Unable to revive", si.UserId) return false } ok, err := item.Consume(si.UserId, round.ReviveCost.ItemId, common.LOGTYPE_SINGLEMATCH_REVIVE, round.ReviveCost.Count, 0) if !ok { errMsg := fmt.Sprintf("not enough fee[%v] for revive err = %s", round.ReviveCost, err) log.Release("singlematchmanager.revive failed %s", errMsg) return false } // 分数 si.UserScore = si.getAverageScore() si.Revived = true si.onUserPromoted(true) return true } func (si *singlematchinstance) getAverageScore() int { if len(si.robotScores) == 0 { return 0 } total := 0 for _, v := range si.robotScores { total += v } return total / len(si.robotScores) } func (si *singlematchinstance) calReviveTimeout() int { if si.reviveBeginTime == 0 { return 0 } ret := si.matchConfig.ReviveTimeoutSec - (int(time.Now().Unix()) - si.reviveBeginTime) if ret < 0 { ret = 0 } return ret }