package sngmatch import ( "os" "bet24.com/log" "bet24.com/servers/common" //gameHistory "bet24.com/servers/micros/gameHistory/proto" "encoding/json" "fmt" "sort" "strconv" "sync" "time" inventory "bet24.com/servers/micros/item_inventory/proto" item "bet24.com/servers/micros/item_inventory/proto" "bet24.com/servers/micros/matches/handler/matchbase" "bet24.com/servers/micros/matches/handler/simplematch" platformconfig "bet24.com/servers/micros/platformconfig/proto" robot "bet24.com/servers/micros/userservices/proto" user "bet24.com/servers/micros/userservices/proto" ) const config_key = "sngmatch_config" const refresh_config_sec = 600 const match_time_out_ended = 120 type sngmatchMgr struct { MatchConfigs []*matchconfig matches map[int][]*matchInfo lock *sync.RWMutex lockConfig *sync.RWMutex } var matchMgr *sngmatchMgr func getMatchManager() *sngmatchMgr { if matchMgr == nil { matchMgr = new(sngmatchMgr) matchMgr.lock = &sync.RWMutex{} matchMgr.lockConfig = &sync.RWMutex{} matchMgr.matches = make(map[int][]*matchInfo) } return matchMgr } func (mm *sngmatchMgr) run() { mm.loadConfig() go mm.checkTimeout() } func (mm *sngmatchMgr) loadConfig() { defer func() { time.AfterFunc(refresh_config_sec*time.Second, mm.loadConfig) }() configString := platformconfig.GetConfig(config_key) if configString == "" { data, err := os.ReadFile("serviceconf/sngmatch.json") if err != nil { log.Release("sngmatch.sngmatchMgr.loadData read json failed") return } configString = string(data) platformconfig.SetConfig(config_key, configString) } var configs []*matchconfig err := json.Unmarshal([]byte(configString), &configs) if err != nil { log.Release("sngmatch.sngmatchMgr.loadData Unmarshal failed err:%v", err) return } for _, v := range configs { v.calTotalPrize() mm.updateConfig(v) } } func (mm *sngmatchMgr) getOnlineUserCount(matchId int) int { ret := 0 mm.lock.RLock() matches, ok := mm.matches[matchId] mm.lock.RUnlock() if !ok { return ret } for _, v := range matches { ret += v.getOnlineUserCount() } return ret } func (mm *sngmatchMgr) updateConfig(config *matchconfig) { mm.lockConfig.Lock() defer mm.lockConfig.Unlock() //for _,v := range mm.MatchConfigs { for i := 0; i < len(mm.MatchConfigs); i++ { v := mm.MatchConfigs[i] if v.MatchId == config.MatchId { config.OnlineUser = v.OnlineUser mm.MatchConfigs[i] = config return } } // 没有 mm.MatchConfigs = append(mm.MatchConfigs, config) } func (mm *sngmatchMgr) checkTimeout() { time.AfterFunc(30*time.Second, mm.checkTimeout) mm.lock.Lock() for k, v := range mm.matches { for i := 0; i < len(v); { if v[i].isTimeout() { v = append(v[:i], v[i+1:]...) } else { i++ } } mm.matches[k] = v } mm.lock.Unlock() } func (mm *sngmatchMgr) dump(param1, param2 string) { log.Release("-------------------------------") log.Release("sngmatch.sngmatchMgr.dump[%s,%s]", param1, param2) defer func() { log.Release("+++++++++++++++++++++++++++++++") log.Release("") }() if param1 == "config" { mm.lockConfig.RLock() defer mm.lockConfig.RUnlock() for _, v := range mm.MatchConfigs { v.dump() } return } if param1 == "history" { getHistoryManager().dump(param2) return } if param1 == "dismiss" { matchNo, err := strconv.Atoi(param2) if err != nil { log.Release("invalid param %s", param2) return } mm.dismissMatch(matchNo) return } mm.lock.RLock() for k, v := range mm.matches { log.Release(" MatchId[%d]", k) for _, v1 := range v { v1.dump() } log.Release(" -------------") } mm.lock.RUnlock() } func (mm *sngmatchMgr) dismissMatch(matchNo int) { mi := mm.getMatchByMatchNo(matchNo) if mi == nil { log.Release("sngmatchMgr.dismissMatch [%d] not found", matchNo) return } config := mm.getConfig(mi.MatchId) if config == nil { log.Release("sngmatchMgr.dismissMatch matchId[%d] not found", mi.MatchId) return } if mi.getStatus() == matchbase.MatchStatus_Ended { log.Release("sngmatchMgr.dismissMatch matchId[%d] ended", mi.MatchId) return } userList := mi.getUserList() for _, v := range userList { // 退钱 if len(config.EnrollFee) > 0 { itm := mi.getUserFee(v, true) if itm.Count > 0 { inventory.AddItems(v, []item.ItemPack{itm}, "sngmatch fee return", common.LOGTYPE_SNGMATCH_ENTER_RETURN) } else if config.DailyFreeCount > 0 { getFreeCountManager().reduceFreeCount(v, config.MatchId) } } } simplematch.DismissMatch(matchNo) } func (mm *sngmatchMgr) enrollSngMatch(matchId int, userId int, nickname string, faceId int, faceUrl string, feeIndex int) (int, string) { config := mm.getConfig(matchId) if config == nil { log.Release("sngmatchMgr.enrollSngMatch matchId[%d] not found", matchId) return 0, "Wrong MatchId" } log.Debug("enrollSngMatch matchId = %d config = %v", matchId, config) if !config.IsInTime() { log.Release("sngmatchMgr.enrollSngMatch matchId[%d] not open %v", matchId, config) return 0, "Not in match time" } if mm.isUserPlaying(userId) { return 0, "Already in a match" } mi := mm.getOrCreateMatch(matchId, config) if mi == nil { log.Release("sngmatchMgr.enrollSngMatch matchId[%d] getOrCreateMatch failed", matchId) return 0, "Serve error" } if feeIndex >= len(config.EnrollFee) { feeIndex = len(config.EnrollFee) - 1 } // 如果有免费次数 if config.DailyFreeCount > 0 && getFreeCountManager().getUserFreeCount(userId, config.MatchId) < config.DailyFreeCount { getFreeCountManager().addFreeCount(userId, config.MatchId) } else { // 扣钱 if len(config.EnrollFee) > 0 && !robot.IsRobot(userId) { ok, err := inventory.Consume(userId, config.EnrollFee[feeIndex].ItemId, common.LOGTYPE_SNGMATCH_ENTER, config.EnrollFee[feeIndex].Count, 0) if !ok { errMsg := fmt.Sprintf("not enough fee[%v] for play err = %s", config.EnrollFee[feeIndex], err) log.Release("sngmatch.enrollSngMatch failed %s", errMsg) return 0, errMsg } mi.setUserFee(userId, config.EnrollFee[feeIndex]) } } mi.addEnrollUser(userId) // 玩家加入 retCode, errMsg := simplematch.EnrollMatch(userId, nickname, faceId, faceUrl, mi.MatchNo) if retCode != 1 { return 0, errMsg } config.addOnline(1) return mi.MatchNo, "OK" } func (mm *sngmatchMgr) quitSngMatch(matchId int, userId int) bool { config := mm.getConfig(matchId) if config == nil { log.Release("sngmatchMgr.quitSngMatch matchId[%d] not found", matchId) return false } // 查询我的比赛 mm.lock.RLock() matches, ok := mm.matches[matchId] mm.lock.RUnlock() if !ok || len(matches) == 0 { log.Release("sngmatchMgr.quitSngMatch matchId[%d] no match found", matchId) return false } for _, v := range matches { if v.getStatus() == matchbase.MatchStatus_Free && v.isUserEnrolled(userId) { // 退钱 if len(config.EnrollFee) > 0 { itm := v.getUserFee(userId, true) if itm.Count > 0 { inventory.AddItems(userId, []item.ItemPack{itm}, "sngmatch fee return", common.LOGTYPE_SNGMATCH_ENTER_RETURN) } else if config.DailyFreeCount > 0 { getFreeCountManager().reduceFreeCount(userId, config.MatchId) } } ok := simplematch.QuitMatch(userId, v.MatchNo) if ok { config.addOnline(-1) } return ok } } return false } func (mm *sngmatchMgr) getConfig(matchId int) *matchconfig { mm.lockConfig.RLock() defer mm.lockConfig.RUnlock() for _, v := range mm.MatchConfigs { if v.MatchId == matchId { return v } } return nil } func (mm *sngmatchMgr) getCurrentMatch(matchId int) *matchInfo { mm.lock.RLock() matches, ok := mm.matches[matchId] mm.lock.RUnlock() if ok { for _, v := range matches { if v.getStatus() == matchbase.MatchStatus_Free && !v.isFull() { return v } } } return nil } func (mm *sngmatchMgr) getOrCreateMatch(matchId int, config *matchconfig) *matchInfo { ret := mm.getCurrentMatch(matchId) if ret != nil { return ret } // 创建一个比赛 matchNo, err := simplematch.CreateMatch(-1, config.GameId, config.GameRule, config.TotalUser, config.Target, config.TableUser, 0, 0, config.PlayTime, config.EleminateByScore, config.WinnerCount) if matchNo == 0 { log.Release("sngmatchMgr.getOrCreateMatch CreateMatch failed %s", err) return nil } mi := simplematch.GetMatchInstance(matchNo) if mi == nil { log.Release("sngmatchMgr.getOrCreateMatch GetMatchInstance == nil") return nil } matchInfo := newMatchInfo(config.MatchId, matchNo, mi, mm, config.RobotConfig) mi.RegisterReceiver(mm) mm.lock.Lock() mm.matches[matchId] = append(mm.matches[matchId], matchInfo) mm.lock.Unlock() return matchInfo } // MatchInstanceReceiver func (mm *sngmatchMgr) OnMatchStart(matchNo int) { log.Debug("sngmatchMgr.OnMatchStart [%d]", matchNo) mi := mm.getMatchByMatchNo(matchNo) if mi == nil { log.Release("sngmatchMgr.OnMatchStart [%d] matchInfo not found", matchNo) return } mi.StartTime = time.Now().Unix() go mi.onMatchStart() } func (mm *sngmatchMgr) OnMatchCancelled(matchNo int) { log.Debug("sngmatchMgr.OnMatchCancelled [%d]", matchNo) mi := mm.getMatchByMatchNo(matchNo) if mi == nil { log.Release("sngmatchMgr.OnMatchCancelled [%d] matchInfo not found", matchNo) return } config := mm.getConfig(mi.MatchId) if config == nil { log.Release("sngmatchMgr.OnMatchCancelled [%d] config not found", matchNo) return } matchInstance := simplematch.GetMatchInstance(matchNo) if matchInstance == nil { log.Release("sngmatchMgr.OnMatchCancelled [%d] matchInstance not found", matchNo) return } // 退费 userList := matchInstance.GetUserList() for _, userId := range userList { // 退钱 if len(config.EnrollFee) > 0 { itm := mi.getUserFee(userId, true) if itm.Count > 0 { inventory.AddItems(userId, []item.ItemPack{itm}, "sngmatch fee return", common.LOGTYPE_SNGMATCH_ENTER_RETURN) } else if config.DailyFreeCount > 0 { getFreeCountManager().reduceFreeCount(userId, config.MatchId) } } } config.addOnline(-len(userList)) } func (mm *sngmatchMgr) OnMatchEnd(matchNo int) { log.Debug("sngmatchMgr.OnMatchEnd [%d]", matchNo) mi := mm.getMatchByMatchNo(matchNo) if mi == nil { log.Release("sngmatchMgr.OnMatchEnd [%d] matchInfo not found", matchNo) return } mi.EndTime = time.Now().Unix() config := mm.getConfig(mi.MatchId) if config == nil { log.Release("sngmatchMgr.OnMatchEnd matchId[%d] not found", mi.MatchId) return } // 找出胜利者给发奖励 matchInstance := simplematch.GetMatchInstance(matchNo) if matchInstance == nil { log.Release("sngmatchMgr.OnMatchEnd [%d] matchInstance not found", matchNo) return } winners := matchInstance.GetWinners() if len(winners) == 0 { log.Release("sngmatchMgr.OnMatchEnd [%d] no winners", matchNo) return } config.addOnline(-config.TotalUser) // 加入历史记录 var h snghistory h.matchconfig = *config h.RobotConfig = nil h.MatchNo = matchNo h.StartTime = mi.StartTime h.EndTime = mi.EndTime h.Winners = winners matchUsers := matchInstance.GetAllMatchUsers() // 所有参与人员 sort.Slice(matchUsers, func(i, j int) bool { return matchUsers[i].Rank < matchUsers[j].Rank }) for _, v := range matchUsers { enrollFee := mi.getUserFee(v.UserId, false) h.EnrollUsers = append(h.EnrollUsers, historyUser{ MatchUserBrief: *v.ToBrief(), enrollFeeItemId: enrollFee.ItemId, enrollFeeCount: enrollFee.Count, enrollTime: int(v.EnrollTime), prize: config.getPrizes(v.Rank), }) // 已淘汰用户不再发奖励 if mi.isUserAwarded(v.UserId) { continue } prize := config.getPrizes(v.Rank) if len(prize) > 0 { inventory.AddItems(v.UserId, prize, "sng_prize", common.LOGTYPE_SNGMATCH_PRIZE) postUserRankNotification(v.UserId, config.MatchId, matchNo, v.Rank, prize) log.Debug("OnMatchEnd 发送通知和奖励 [%d] %v", v.UserId, prize) mi.setUserAwarded(v.UserId) } /*gameHistory.MyMatch_Write(v.UserId, &gameHistory.MyMatchInfo{ UserId: v.UserId, //int `json:"UserId, omitempty"` // 用户ID NickName: v.NickName, //string `json:"NickName, omitempty"` // 昵称 MatchName: config.Name, //string // 比赛名称 Rank: v.Rank, //int // 名次 Items: prize, //int // 道具ID })*/ } // 最后把所有报名记录都加上 h.setAllEnrolledUsers(mi.enrolledUsers) getHistoryManager().addHistory(h) go writeRoomRecordToDB(&h) } func (mm *sngmatchMgr) OnUserEliminated(matchNo int, userId int, rank int) { log.Debug("sngmatchMgr.OnUserEliminated [%d] UserId[%d] Rank[%d]", matchNo, userId, rank) mi := mm.getMatchByMatchNo(matchNo) if mi == nil { log.Release("combomatchMgr.OnUserEliminated [%d] matchInfo not found", matchNo) return } config := mm.getConfig(mi.MatchId) if config == nil { log.Release("combomatchMgr.OnUserEliminated matchId[%d] not found", mi.MatchId) return } prize := config.getPrizes(rank) if len(prize) > 0 { inventory.AddItems(userId, prize, "配置赛奖励", common.LOGTYPE_COMBOMATCH_PRIZE) postUserRankNotification(userId, config.MatchId, matchNo, rank, prize) log.Debug("OnMatchEnd 发送通知和奖励 [%d] %v", userId, prize) } mi.setUserAwarded(userId) /*usr := mi.getUser(userId) var myInfo gameHistory.MyMatchInfo myInfo.UserId = userId myInfo.MatchName = config.Name myInfo.Rank = rank myInfo.Items = prize if usr != nil { myInfo.NickName = usr.NickName } gameHistory.MyMatch_Write(userId, &myInfo)*/ } func (mm *sngmatchMgr) getMatchByMatchNo(matchNo int) *matchInfo { mm.lock.RLock() defer mm.lock.RUnlock() for _, v := range mm.matches { for _, v1 := range v { if v1.MatchNo == matchNo { return v1 } } } return nil } func (mm *sngmatchMgr) getMatchList(userId int) string { var configs []matchconfig mm.lockConfig.RLock() for _, v := range mm.MatchConfigs { configs = append(configs, *v) } mm.lockConfig.RUnlock() for i := 0; i < len(configs); i++ { configs[i].setLeftFreeCount(userId) } d, _ := json.Marshal(configs) return string(d) } func (mm *sngmatchMgr) getUserMatchId(userId int) int { mm.lock.RLock() defer mm.lock.RUnlock() for _, v := range mm.matches { for _, v1 := range v { if v1.isUserPlaying(userId) { return v1.MatchId } } } return 0 } func (mm *sngmatchMgr) addARobot(matchId int) bool { m := mm.getCurrentMatch(matchId) if m == nil { log.Release("sngmatchMgr.addARobot matchId[%d] has no live match", matchId) return false } loopCount := 0 for { loopCount++ if loopCount > 20 { log.Release("sngmatchMgr.addARobot matchId[%d] get a robot failed", matchId) return false } robotId := robot.GetARobot() if mm.isUserPlaying(robotId) { continue } usr := user.GetUserInfo(robotId) if usr == nil { continue } mm.enrollSngMatch(matchId, robotId, usr.NickName, usr.FaceId, usr.FaceUrl, 0) break } return true } func (mm *sngmatchMgr) isUserPlaying(userId int) bool { mm.lock.RLock() defer mm.lock.RUnlock() for _, m := range mm.matches { for _, mi := range m { if mi.isUserPlaying(userId) { return true } } } return false }