package simplematch import ( "encoding/json" "fmt" "os" "strconv" "sync" "time" "bet24.com/log" "bet24.com/servers/common" micro_common "bet24.com/servers/micros/common" "bet24.com/servers/micros/matches/handler/matchbase" cash "bet24.com/servers/micros/money/proto" platformconfig "bet24.com/servers/micros/platformconfig/proto" privateroom "bet24.com/servers/micros/privateroom/proto" ) const ( match_timeout_not_playing = 1800 match_timeout_started = 14400 match_timeout_ended = 120 match_round_timeoff = 30 // 轮次之间休息时间 ) const config_key = "simplematch_config" const refresh_config_sec = 600 type matchmanager struct { lock *sync.RWMutex matches map[int]*matchinstance TaxRate int Timeout_Play int64 Timeout_Free int64 Timeout_End int64 MatchConfigs []*matchconfig } var mgr *matchmanager func getMatchManager() *matchmanager { if mgr == nil { mgr = new(matchmanager) mgr.ctor() } return mgr } func (mm *matchmanager) ctor() { log.Debug("matchmanager.ctor") } func (mm *matchmanager) run() { mm.lock = &sync.RWMutex{} mm.matches = make(map[int]*matchinstance) mm.loadConfig() log.Release("simplematchmanager.run taxRate = %d", mm.TaxRate) go mm.checkTimeout() go privateroom.RegisterServerStatus(mm, mm, micro_common.GetServicePort(), "simplematch") } func (mm *matchmanager) loadConfig() { defer func() { time.AfterFunc(refresh_config_sec*time.Second, mm.loadConfig) }() configString := platformconfig.GetConfig(config_key) if configString == "" { data, err := os.ReadFile("serviceconf/simplematch.json") if err != nil { log.Release("simplematch.simplamatchmanager.loadData read json failed") return } configString = string(data) platformconfig.SetConfig(config_key, configString) } err := json.Unmarshal([]byte(configString), mm) if err != nil { log.Release("simplematch.simplamatchmanager.loadData Unmarshal failed err:%v", err) return } //log.Release("matchmanager.loadConfig ok %v", mm.MatchConfigs) if mm.Timeout_Free == 0 { mm.Timeout_Free = match_timeout_not_playing } if mm.Timeout_Play == 0 { mm.Timeout_Play = match_timeout_started } if mm.Timeout_End == 0 { mm.Timeout_End = match_timeout_ended } for _, v := range mm.MatchConfigs { if v.Round_timeoff == 0 { v.Round_timeoff = match_round_timeoff } } } func (mm *matchmanager) checkTimeout() { time.AfterFunc(30*time.Second, mm.checkTimeout) var toRemove []int mm.lock.RLock() for _, v := range mm.matches { if v.isTimeout(mm.Timeout_Free, mm.Timeout_Play, mm.Timeout_End) { toRemove = append(toRemove, v.MatchNo) } } mm.lock.RUnlock() if len(toRemove) <= 0 { return } log.Debug("simplematchmanager.checkTimeout removing matches %v", toRemove) for _, v := range toRemove { mm.closeMatch(v, "timeout") mm.lock.Lock() delete(mm.matches, v) mm.lock.Unlock() } } func (mm *matchmanager) getMatchNumber() int { return matchbase.GetMatchNo() } func (mm *matchmanager) getMatch(matchNo int) *matchinstance { //log.Debug("simplematch.matchmanager.getMatch [%d]", matchNo) mm.lock.RLock() mi, ok := mm.matches[matchNo] mm.lock.RUnlock() if !ok { //log.Debug("simplematch.matchmanager.getMatch [%d] 2", matchNo) return nil } //log.Debug("simplematch.matchmanager.getMatch [%d] 3", matchNo) return mi } func (mm *matchmanager) getMatchInfo(matchNo int) string { mi := mm.getMatch(matchNo) if mi == nil { return "" } d, _ := json.Marshal(mi) return string(d) } func (mm *matchmanager) dismissMatch(matchNo int) { mi := mm.getMatch(matchNo) if mi == nil { log.Release("matchmanager.dismissMatch failed matchNo[%d] not exist", matchNo) return } mi.dismiss() } func (mm *matchmanager) closeMatchByOwner(userId int, matchNo int) (bool, string) { mi := mm.getMatch(matchNo) if mi == nil { log.Release("matchmanager.closeMatch failed matchNo[%d] not exist", matchNo) return false, "Invalid MatchNo" } ok, errMsg := mi.closeByOwner(userId) return ok, errMsg } func (mm *matchmanager) closeMatch(matchNo int, reason string) (bool, string) { mi := mm.getMatch(matchNo) if mi == nil { log.Release("matchmanager.closeMatch failed matchNo[%d] not exist", matchNo) return false, "Invalid MatchNo" } mi.closeMatch(reason) return true, "ok" } func (mm *matchmanager) createMatch(userId int, gameId int, gameRule string, totalUserCount int, target int, tableUserCount int, enrollFee int, prize int, playTimeout int, eliminatedByScore bool, winnerCount int) (matchNo int, errMsg string) { matchNo = 0 errMsg = "ok" // 自己校验 mc := mm.getGameRule(gameId) if mc == nil { log.Release("matchmanager.createMatch gameId[%d] not config match", gameId) errMsg = "Invalid GameId" return } //log.Release("simplematch.matchmanager.createMatch gameId[%d]", gameId) //mc.dump() if !mc.isValidParams(gameRule, totalUserCount, tableUserCount, target, playTimeout) { errMsg = "Invalid GameRule" return } /* 看下规则是否存在 if !privateroom.IsGameRuleValid(gameId, gameRule, target, tableUserCount) { errMsg = "Invalid GameRule" return }*/ // 扣钱,userId == -1 表示系统创建 if prize > 0 && userId > 0 && !cash.ReduceMoney(userId, prize, common.LOGTYPE_SIMPLEMATCH_CREATE, "simplematch", "create", "") { errMsg = fmt.Sprintf("not enough cash for create match prize[%d]", prize) log.Release("createMatch failed %s", errMsg) return } matchNo = mm.getMatchNumber() mi := newMatchInstance(matchNo, userId, gameId, gameRule, totalUserCount, target, tableUserCount, enrollFee, prize, playTimeout, mc, eliminatedByScore, winnerCount) mm.lock.Lock() mm.matches[matchNo] = mi mm.lock.Unlock() return } func (mm *matchmanager) enrollMatch(userId int, nickname string, faceId int, faceUrl string, matchNo int) (bool, string) { mi := mm.getMatch(matchNo) if mi == nil { log.Release("matchmanager.enrollMatch user[%d] matchNo[%d] not exist", userId, matchNo) return false, fmt.Sprintf("MatchNo[%d] not found", matchNo) } ok, err := mi.addUser(userId, nickname, faceId, faceUrl, 0) if ok { mi.postUserEnterNotification(userId, nickname, faceId, faceUrl, 0) return ok, err } return ok, err } func (mm *matchmanager) enrollMatchWithInitialScore(userId int, nickname string, faceId int, faceUrl string, matchNo int, score int) (bool, string) { mi := mm.getMatch(matchNo) if mi == nil { log.Release("matchmanager.enrollMatch user[%d] matchNo[%d] not exist", userId, matchNo) return false, fmt.Sprintf("MatchNo[%d] not found", matchNo) } ok, err := mi.addUser(userId, nickname, faceId, faceUrl, score) if ok { mi.postUserEnterNotification(userId, nickname, faceId, faceUrl, score) return ok, err } return ok, err } func (mm *matchmanager) quitMatch(userId int, matchNo int) bool { mi := mm.getMatch(matchNo) if mi == nil { log.Release("matchmanager.quitMatch user[%d] matchNo[%d] not exist", userId, matchNo) return false } if mi.removeUser(userId) { mi.postUserExitNotification(userId) return true } return false } func (mm *matchmanager) getUserMatches(userId int) string { var ret []matchinstance mm.lock.RLock() for _, v := range mm.matches { if v.Owner == userId { ret = append(ret, *v) } } mm.lock.RUnlock() d, _ := json.Marshal(ret) return string(d) } func (mm *matchmanager) dump(param1, param2 string) { log.Release("-------------------------------") log.Release("simplematch.matchmanager.dump[%s,%s]", param1, param2) defer func() { log.Release("+++++++++++++++++++++++++++++++") log.Release("") }() if param1 == "rules" { for _, v := range mm.MatchConfigs { v.dump() } return } if param1 == "" { mm.lock.RLock() for _, v := range mm.matches { v.dump() } mm.lock.RUnlock() return } matchNo, err := strconv.Atoi(param1) if err != nil { log.Release(" atoi error %v", err) return } match := mm.getMatch(matchNo) if match == nil { log.Release(" match[%d] not exist", matchNo) return } match.dump() } func (mm *matchmanager) OnGameRuleRegistered(gameId int, gameRule string, desc string, targetOptions []int, userOptions []int, playTimeOptions []int) { for _, v := range mm.MatchConfigs { if v.GameId == gameId { v.addGameRule(gameRule, desc, targetOptions, userOptions, playTimeOptions) } } } func (mm *matchmanager) OnGameRuleDeregistered(gameId int, gameRule string) { log.Debug("matchmanager.OnGameRuleDeregistered gameId[%d] gameRule[%s]", gameId, gameRule) } func (mm *matchmanager) getGameRule(gameId int) *matchconfig { for _, v := range mm.MatchConfigs { if v.GameId == gameId { return v } } return nil } func (mm *matchmanager) getMatchConfigs() string { d, _ := json.Marshal(mm.MatchConfigs) return string(d) } func (mm *matchmanager) getEnrolledMatch(userId int) string { var matches []int mm.lock.RLock() for _, v := range mm.matches { if v.getUser(userId) != nil { matches = append(matches, v.MatchNo) } } mm.lock.RUnlock() d, _ := json.Marshal(matches) return string(d) } func (mm *matchmanager) getMatchInstance(matchNo int) matchbase.MatchInstance { ret := mm.getMatch(matchNo) if ret == nil { log.Debug("simplematch.matchmanager.getMatchInstance [%d] is nil", matchNo) return nil } return ret } func (mm *matchmanager) OnRoomStart(roomNo int) { //log.Debug("simplematchmanager.OnRoomStart %d", roomNo) mm.lock.RLock() for _, v := range mm.matches { go func(m *matchinstance) { m.OnRoomStart(roomNo) }(v) } mm.lock.RUnlock() } func (mm *matchmanager) OnRoomEnd(roomNo int, winners []int) { //log.Debug("simplematchmanager.OnRoomEnd %d winners%v", roomNo, winners) mm.lock.RLock() for _, v := range mm.matches { go func(m *matchinstance) { m.OnRoomEnd(roomNo, winners) }(v) } mm.lock.RUnlock() } func (mm *matchmanager) OnRoomUserScoreChanged(roomNo int, userId int, score int) { //log.Debug("simplematchmanager.OnRoomUserScoreChanged %d UserId[%d] score[%d]", roomNo, userId, score) mm.lock.RLock() for _, v := range mm.matches { go func(m *matchinstance) { m.OnRoomUserScoreChanged(roomNo, userId, score) }(v) } mm.lock.RUnlock() } func (mm *matchmanager) OnRoomStatusChanged(roomNo int, old, new int) { //log.Debug("simplematchmanager.OnRoomStatusChanged %d %d->%d", roomNo, old, new) mm.lock.RLock() for _, v := range mm.matches { go func(m *matchinstance) { m.OnRoomStatusChanged(roomNo, old, new) }(v) } mm.lock.RUnlock() }