simplematchmanager.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. package simplematch
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "strconv"
  7. "sync"
  8. "time"
  9. "bet24.com/log"
  10. "bet24.com/servers/common"
  11. micro_common "bet24.com/servers/micros/common"
  12. "bet24.com/servers/micros/matches/handler/matchbase"
  13. cash "bet24.com/servers/micros/money/proto"
  14. platformconfig "bet24.com/servers/micros/platformconfig/proto"
  15. privateroom "bet24.com/servers/micros/privateroom/proto"
  16. )
  17. const (
  18. match_timeout_not_playing = 1800
  19. match_timeout_started = 14400
  20. match_timeout_ended = 120
  21. match_round_timeoff = 30 // 轮次之间休息时间
  22. )
  23. const config_key = "simplematch_config"
  24. const refresh_config_sec = 600
  25. type matchmanager struct {
  26. lock *sync.RWMutex
  27. matches map[int]*matchinstance
  28. TaxRate int
  29. Timeout_Play int64
  30. Timeout_Free int64
  31. Timeout_End int64
  32. MatchConfigs []*matchconfig
  33. }
  34. var mgr *matchmanager
  35. func getMatchManager() *matchmanager {
  36. if mgr == nil {
  37. mgr = new(matchmanager)
  38. mgr.ctor()
  39. }
  40. return mgr
  41. }
  42. func (mm *matchmanager) ctor() {
  43. log.Debug("matchmanager.ctor")
  44. }
  45. func (mm *matchmanager) run() {
  46. mm.lock = &sync.RWMutex{}
  47. mm.matches = make(map[int]*matchinstance)
  48. mm.loadConfig()
  49. log.Release("simplematchmanager.run taxRate = %d", mm.TaxRate)
  50. go mm.checkTimeout()
  51. go privateroom.RegisterServerStatus(mm, mm, micro_common.GetServicePort(), "simplematch")
  52. }
  53. func (mm *matchmanager) loadConfig() {
  54. defer func() {
  55. time.AfterFunc(refresh_config_sec*time.Second, mm.loadConfig)
  56. }()
  57. configString := platformconfig.GetConfig(config_key)
  58. if configString == "" {
  59. data, err := os.ReadFile("serviceconf/simplematch.json")
  60. if err != nil {
  61. log.Release("simplematch.simplamatchmanager.loadData read json failed")
  62. return
  63. }
  64. configString = string(data)
  65. platformconfig.SetConfig(config_key, configString)
  66. }
  67. err := json.Unmarshal([]byte(configString), mm)
  68. if err != nil {
  69. log.Release("simplematch.simplamatchmanager.loadData Unmarshal failed err:%v", err)
  70. return
  71. }
  72. //log.Release("matchmanager.loadConfig ok %v", mm.MatchConfigs)
  73. if mm.Timeout_Free == 0 {
  74. mm.Timeout_Free = match_timeout_not_playing
  75. }
  76. if mm.Timeout_Play == 0 {
  77. mm.Timeout_Play = match_timeout_started
  78. }
  79. if mm.Timeout_End == 0 {
  80. mm.Timeout_End = match_timeout_ended
  81. }
  82. for _, v := range mm.MatchConfigs {
  83. if v.Round_timeoff == 0 {
  84. v.Round_timeoff = match_round_timeoff
  85. }
  86. }
  87. }
  88. func (mm *matchmanager) checkTimeout() {
  89. time.AfterFunc(30*time.Second, mm.checkTimeout)
  90. var toRemove []int
  91. mm.lock.RLock()
  92. for _, v := range mm.matches {
  93. if v.isTimeout(mm.Timeout_Free, mm.Timeout_Play, mm.Timeout_End) {
  94. toRemove = append(toRemove, v.MatchNo)
  95. }
  96. }
  97. mm.lock.RUnlock()
  98. if len(toRemove) <= 0 {
  99. return
  100. }
  101. log.Debug("simplematchmanager.checkTimeout removing matches %v", toRemove)
  102. for _, v := range toRemove {
  103. mm.closeMatch(v, "timeout")
  104. mm.lock.Lock()
  105. delete(mm.matches, v)
  106. mm.lock.Unlock()
  107. }
  108. }
  109. func (mm *matchmanager) getMatchNumber() int {
  110. return matchbase.GetMatchNo()
  111. }
  112. func (mm *matchmanager) getMatch(matchNo int) *matchinstance {
  113. //log.Debug("simplematch.matchmanager.getMatch [%d]", matchNo)
  114. mm.lock.RLock()
  115. mi, ok := mm.matches[matchNo]
  116. mm.lock.RUnlock()
  117. if !ok {
  118. //log.Debug("simplematch.matchmanager.getMatch [%d] 2", matchNo)
  119. return nil
  120. }
  121. //log.Debug("simplematch.matchmanager.getMatch [%d] 3", matchNo)
  122. return mi
  123. }
  124. func (mm *matchmanager) getMatchInfo(matchNo int) string {
  125. mi := mm.getMatch(matchNo)
  126. if mi == nil {
  127. return ""
  128. }
  129. d, _ := json.Marshal(mi)
  130. return string(d)
  131. }
  132. func (mm *matchmanager) dismissMatch(matchNo int) {
  133. mi := mm.getMatch(matchNo)
  134. if mi == nil {
  135. log.Release("matchmanager.dismissMatch failed matchNo[%d] not exist", matchNo)
  136. return
  137. }
  138. mi.dismiss()
  139. }
  140. func (mm *matchmanager) closeMatchByOwner(userId int, matchNo int) (bool, string) {
  141. mi := mm.getMatch(matchNo)
  142. if mi == nil {
  143. log.Release("matchmanager.closeMatch failed matchNo[%d] not exist", matchNo)
  144. return false, "Invalid MatchNo"
  145. }
  146. ok, errMsg := mi.closeByOwner(userId)
  147. return ok, errMsg
  148. }
  149. func (mm *matchmanager) closeMatch(matchNo int, reason string) (bool, string) {
  150. mi := mm.getMatch(matchNo)
  151. if mi == nil {
  152. log.Release("matchmanager.closeMatch failed matchNo[%d] not exist", matchNo)
  153. return false, "Invalid MatchNo"
  154. }
  155. mi.closeMatch(reason)
  156. return true, "ok"
  157. }
  158. func (mm *matchmanager) createMatch(userId int, gameId int, gameRule string, totalUserCount int, target int,
  159. tableUserCount int, enrollFee int, prize int, playTimeout int, eliminatedByScore bool, winnerCount int) (matchNo int, errMsg string) {
  160. matchNo = 0
  161. errMsg = "ok"
  162. // 自己校验
  163. mc := mm.getGameRule(gameId)
  164. if mc == nil {
  165. log.Release("matchmanager.createMatch gameId[%d] not config match", gameId)
  166. errMsg = "Invalid GameId"
  167. return
  168. }
  169. //log.Release("simplematch.matchmanager.createMatch gameId[%d]", gameId)
  170. //mc.dump()
  171. if !mc.isValidParams(gameRule, totalUserCount, tableUserCount, target, playTimeout) {
  172. errMsg = "Invalid GameRule"
  173. return
  174. }
  175. /* 看下规则是否存在
  176. if !privateroom.IsGameRuleValid(gameId, gameRule, target, tableUserCount) {
  177. errMsg = "Invalid GameRule"
  178. return
  179. }*/
  180. // 扣钱,userId == -1 表示系统创建
  181. if prize > 0 && userId > 0 && !cash.ReduceMoney(userId, prize, common.LOGTYPE_SIMPLEMATCH_CREATE, "simplematch", "create", "") {
  182. errMsg = fmt.Sprintf("not enough cash for create match prize[%d]", prize)
  183. log.Release("createMatch failed %s", errMsg)
  184. return
  185. }
  186. matchNo = mm.getMatchNumber()
  187. mi := newMatchInstance(matchNo, userId, gameId, gameRule, totalUserCount, target,
  188. tableUserCount, enrollFee, prize, playTimeout, mc, eliminatedByScore, winnerCount)
  189. mm.lock.Lock()
  190. mm.matches[matchNo] = mi
  191. mm.lock.Unlock()
  192. return
  193. }
  194. func (mm *matchmanager) enrollMatch(userId int, nickname string, faceId int, faceUrl string, matchNo int) (bool, string) {
  195. mi := mm.getMatch(matchNo)
  196. if mi == nil {
  197. log.Release("matchmanager.enrollMatch user[%d] matchNo[%d] not exist", userId, matchNo)
  198. return false, fmt.Sprintf("MatchNo[%d] not found", matchNo)
  199. }
  200. ok, err := mi.addUser(userId, nickname, faceId, faceUrl, 0)
  201. if ok {
  202. mi.postUserEnterNotification(userId, nickname, faceId, faceUrl, 0)
  203. return ok, err
  204. }
  205. return ok, err
  206. }
  207. func (mm *matchmanager) enrollMatchWithInitialScore(userId int, nickname string, faceId int, faceUrl string, matchNo int, score int) (bool, string) {
  208. mi := mm.getMatch(matchNo)
  209. if mi == nil {
  210. log.Release("matchmanager.enrollMatch user[%d] matchNo[%d] not exist", userId, matchNo)
  211. return false, fmt.Sprintf("MatchNo[%d] not found", matchNo)
  212. }
  213. ok, err := mi.addUser(userId, nickname, faceId, faceUrl, score)
  214. if ok {
  215. mi.postUserEnterNotification(userId, nickname, faceId, faceUrl, score)
  216. return ok, err
  217. }
  218. return ok, err
  219. }
  220. func (mm *matchmanager) quitMatch(userId int, matchNo int) bool {
  221. mi := mm.getMatch(matchNo)
  222. if mi == nil {
  223. log.Release("matchmanager.quitMatch user[%d] matchNo[%d] not exist", userId, matchNo)
  224. return false
  225. }
  226. if mi.removeUser(userId) {
  227. mi.postUserExitNotification(userId)
  228. return true
  229. }
  230. return false
  231. }
  232. func (mm *matchmanager) getUserMatches(userId int) string {
  233. var ret []matchinstance
  234. mm.lock.RLock()
  235. for _, v := range mm.matches {
  236. if v.Owner == userId {
  237. ret = append(ret, *v)
  238. }
  239. }
  240. mm.lock.RUnlock()
  241. d, _ := json.Marshal(ret)
  242. return string(d)
  243. }
  244. func (mm *matchmanager) dump(param1, param2 string) {
  245. log.Release("-------------------------------")
  246. log.Release("simplematch.matchmanager.dump[%s,%s]", param1, param2)
  247. defer func() {
  248. log.Release("+++++++++++++++++++++++++++++++")
  249. log.Release("")
  250. }()
  251. if param1 == "rules" {
  252. for _, v := range mm.MatchConfigs {
  253. v.dump()
  254. }
  255. return
  256. }
  257. if param1 == "" {
  258. mm.lock.RLock()
  259. for _, v := range mm.matches {
  260. v.dump()
  261. }
  262. mm.lock.RUnlock()
  263. return
  264. }
  265. matchNo, err := strconv.Atoi(param1)
  266. if err != nil {
  267. log.Release(" atoi error %v", err)
  268. return
  269. }
  270. match := mm.getMatch(matchNo)
  271. if match == nil {
  272. log.Release(" match[%d] not exist", matchNo)
  273. return
  274. }
  275. match.dump()
  276. }
  277. func (mm *matchmanager) OnGameRuleRegistered(gameId int, gameRule string, desc string, targetOptions []int, userOptions []int, playTimeOptions []int) {
  278. for _, v := range mm.MatchConfigs {
  279. if v.GameId == gameId {
  280. v.addGameRule(gameRule, desc, targetOptions, userOptions, playTimeOptions)
  281. }
  282. }
  283. }
  284. func (mm *matchmanager) OnGameRuleDeregistered(gameId int, gameRule string) {
  285. log.Debug("matchmanager.OnGameRuleDeregistered gameId[%d] gameRule[%s]", gameId, gameRule)
  286. }
  287. func (mm *matchmanager) getGameRule(gameId int) *matchconfig {
  288. for _, v := range mm.MatchConfigs {
  289. if v.GameId == gameId {
  290. return v
  291. }
  292. }
  293. return nil
  294. }
  295. func (mm *matchmanager) getMatchConfigs() string {
  296. d, _ := json.Marshal(mm.MatchConfigs)
  297. return string(d)
  298. }
  299. func (mm *matchmanager) getEnrolledMatch(userId int) string {
  300. var matches []int
  301. mm.lock.RLock()
  302. for _, v := range mm.matches {
  303. if v.getUser(userId) != nil {
  304. matches = append(matches, v.MatchNo)
  305. }
  306. }
  307. mm.lock.RUnlock()
  308. d, _ := json.Marshal(matches)
  309. return string(d)
  310. }
  311. func (mm *matchmanager) getMatchInstance(matchNo int) matchbase.MatchInstance {
  312. ret := mm.getMatch(matchNo)
  313. if ret == nil {
  314. log.Debug("simplematch.matchmanager.getMatchInstance [%d] is nil", matchNo)
  315. return nil
  316. }
  317. return ret
  318. }
  319. func (mm *matchmanager) OnRoomStart(roomNo int) {
  320. //log.Debug("simplematchmanager.OnRoomStart %d", roomNo)
  321. mm.lock.RLock()
  322. for _, v := range mm.matches {
  323. go func(m *matchinstance) {
  324. m.OnRoomStart(roomNo)
  325. }(v)
  326. }
  327. mm.lock.RUnlock()
  328. }
  329. func (mm *matchmanager) OnRoomEnd(roomNo int, winners []int) {
  330. //log.Debug("simplematchmanager.OnRoomEnd %d winners%v", roomNo, winners)
  331. mm.lock.RLock()
  332. for _, v := range mm.matches {
  333. go func(m *matchinstance) {
  334. m.OnRoomEnd(roomNo, winners)
  335. }(v)
  336. }
  337. mm.lock.RUnlock()
  338. }
  339. func (mm *matchmanager) OnRoomUserScoreChanged(roomNo int, userId int, score int) {
  340. //log.Debug("simplematchmanager.OnRoomUserScoreChanged %d UserId[%d] score[%d]", roomNo, userId, score)
  341. mm.lock.RLock()
  342. for _, v := range mm.matches {
  343. go func(m *matchinstance) {
  344. m.OnRoomUserScoreChanged(roomNo, userId, score)
  345. }(v)
  346. }
  347. mm.lock.RUnlock()
  348. }
  349. func (mm *matchmanager) OnRoomStatusChanged(roomNo int, old, new int) {
  350. //log.Debug("simplematchmanager.OnRoomStatusChanged %d %d->%d", roomNo, old, new)
  351. mm.lock.RLock()
  352. for _, v := range mm.matches {
  353. go func(m *matchinstance) {
  354. m.OnRoomStatusChanged(roomNo, old, new)
  355. }(v)
  356. }
  357. mm.lock.RUnlock()
  358. }