| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- 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
- }
|