package game import ( "bet24.com/log" "bet24.com/servers/common" "bet24.com/servers/micros/audioroom/handler/config" "bet24.com/servers/micros/audioroom/handler/income" pb "bet24.com/servers/micros/audioroom/proto" micro_common "bet24.com/servers/micros/common" item "bet24.com/servers/micros/item_inventory/proto" privateroom "bet24.com/servers/micros/privateroom/proto" "bet24.com/servers/zego" "encoding/json" "fmt" "math/rand" "strconv" "sync" "time" ) var mgr *gamemgr type gameRoom struct { pb.GameRoomInfo RoomId int // 属于哪个语聊房 } type removedGameRoom struct { gameRoom removeTime int64 } type gamemgr struct { roomlist map[int]*gameRoom lock *sync.RWMutex onRoomEnd func(roomId int, roomNo int) onRoomInfo func(roomId int) *pb.RoomInfo removedRooms map[int]*removedGameRoom lockRemovedRooms *sync.RWMutex } func getGameManager() *gamemgr { if mgr == nil { mgr = new(gamemgr) mgr.ctor() } return mgr } func (this *gamemgr) ctor() { this.lock = &sync.RWMutex{} this.roomlist = make(map[int]*gameRoom) this.lockRemovedRooms = &sync.RWMutex{} this.removedRooms = make(map[int]*removedGameRoom) go privateroom.RegisterServerStatus(this, this, micro_common.GetServicePort(), "audioroom_game") this.checkRemovedRoom() } func (this *gamemgr) setRoomEndSubscriber(onRoomEnd func(roomId int, roomNo int)) { this.onRoomEnd = onRoomEnd } // 设置房间信息订阅 func (this *gamemgr) setRoomInfoSubscriber(onRoomInfo func(roomId int) *pb.RoomInfo) { this.onRoomInfo = onRoomInfo } // 获取房间信息 func (this *gamemgr) getRoomInfo(roomId int) *pb.RoomInfo { if this.onRoomInfo == nil { return nil } return this.onRoomInfo(roomId) } func (this *gamemgr) dump(cmd, param2 string) { switch cmd { case "rule": this.dumpRules() case "user": this.dumpUser(param2) default: this.dumpRoom(param2) } } func (this *gamemgr) dumpUser(param string) { log.Release("-------------------------------") log.Release("gamemgr.dumpUser %s", param) defer func() { log.Release("+++++++++++++++++++++++++++++++") log.Release("") }() userId, err := strconv.Atoi(param) if err != nil { log.Release(" atoi error %v", err) return } rooms := this.getRoomsByRoomId(userId) if len(rooms) == 0 { log.Release(" no room") return } for _, v := range rooms { log.Release(" RoomId[%d]:RoomNo[%d]Addr[%s]Table[%d]Rule[%s]", v.RoomId, v.RoomNo, v.ServerAddr, v.TableId, v.RuleName) } } func (this *gamemgr) dumpRules() { log.Release("-------------------------------") log.Release("gamemgr.dumpRules") defer func() { log.Release("+++++++++++++++++++++++++++++++") log.Release("") }() gameList := config.Mgr.GetGameList() for _, v := range gameList { log.Release(" Id[%d]Name[%s]Rules%v", v.Id, v.Name, v.GameRules) } } func (this *gamemgr) dumpRoom(param string) { log.Release("-------------------------------") log.Release("gamemgr.dumpRoom %s", param) defer func() { log.Release("+++++++++++++++++++++++++++++++") log.Release("") }() if param == "" { this.lock.RLock() for _, v := range this.roomlist { log.Release(" RoomId[%d]RoomNo[%d]Addr[%s]Table[%d]Rule[%s]", v.RoomId, v.RoomNo, v.ServerAddr, v.TableId, v.RuleName) } this.lock.RUnlock() return } roomNo, err := strconv.Atoi(param) if err != nil { log.Release(" atoi error %v", err) return } this.lock.RLock() v, ok := this.roomlist[roomNo] this.lock.RUnlock() if v == nil || !ok { log.Release(" roomNo[%d] not found", roomNo) return } log.Release(" RoomNo[%d]Addr[%s]Table[%d]Rule[%s]", v.RoomNo, v.ServerAddr, v.TableId, v.RuleName) } func (this *gamemgr) getRoomsByRoomId(roomId int) []*gameRoom { var ret []*gameRoom this.lock.RLock() for k, v := range this.roomlist { if v.RoomId == roomId { ret = append(ret, this.roomlist[k]) } } this.lock.RUnlock() return ret } func (this *gamemgr) getRoom(roomNo int) *gameRoom { this.lock.RLock() gr, ok := this.roomlist[roomNo] this.lock.RUnlock() if !ok { return nil } return gr } func (this *gamemgr) getRoomByRoomIdAndRuleName(roomId int, gameId int, ruleName string) *gameRoom { if ruleName == "" || !config.Mgr.IsGameRuleExist(gameId, ruleName) { newRuleName := config.Mgr.GetDefaultGameRule(gameId) log.Debug("gamemgr.createGameRoom gameId[%d] ruleName[%s] changed to [%s]", gameId, ruleName, newRuleName) ruleName = newRuleName } this.lock.RLock() defer this.lock.RUnlock() for k, v := range this.roomlist { if v.RoomId == roomId && v.GameId == gameId && v.RuleName == ruleName { return this.roomlist[k] } } return nil } func (this *gamemgr) createGameRoom(roomId int, gameId int, ruleName string) (bool, *gameRoom) { if ruleName == "" || !config.Mgr.IsGameRuleExist(gameId, ruleName) { newRuleName := config.Mgr.GetDefaultGameRule(gameId) log.Debug("gamemgr.createGameRoom gameId[%d] ruleName[%s] changed to [%s]", gameId, ruleName, newRuleName) ruleName = newRuleName } log.Debug("gamemgr.createGameRoom roomId[%d],gameId[%d],ruleName[%s]", roomId, gameId, ruleName) roomNo, errMsg := privateroom.CreatePrivateRoomByUser(roomId, gameId, ruleName, 0, 1, 0, 0, "audio_game_room", 0, false, "") if roomNo <= 0 { log.Release("gamemgr.createGameRoom CreatePrivateRoomByUser failed %s", errMsg) return false, nil } roomInfoStr := privateroom.GetPrivateRoomInfo(roomNo) var roomInfo privateroom.RoomInfo err := json.Unmarshal([]byte(roomInfoStr), &roomInfo) if err != nil { log.Release("gamemgr.createGameRoom GetPrivateRoomInfo Unmarshal failed [%s] %v", roomInfoStr, err) return false, nil } gr := &gameRoom{ RoomId: roomId, GameRoomInfo: pb.GameRoomInfo{ GameId: gameId, RuleName: ruleName, RoomNo: roomNo, ServerAddr: roomInfo.ServerAddr, TableId: roomInfo.TableId, }, } this.lock.Lock() this.roomlist[roomNo] = gr this.lock.Unlock() return true, gr } // 上报战绩 func (this *gamemgr) reportUserBet(roomId, roomNo, gameId int, roomName string, isChipRoom bool, userBets []pb.UserBet, roomOwner int) { gr := this.getRoom(roomNo) if gr == nil { gr = this.getRemovedRoom(roomNo) } if gr == nil || gr.RoomId != roomId { return } // 道具类型 itemType := item.Item_Gold if isChipRoom { itemType = item.Item_Chip } // 给房主加分成 for _, v := range userBets { income.TriggerGameIncome(v.UserId, pb.TriggerData{ RoomId: roomId, GameId: gameId, RoomOwner: roomOwner, ItemType: itemType, Amount: v.TotalBet, }) } } func (this *gamemgr) getGameRoomInfo(roomId, gameId int, ruleName string) *pb.GameRoomInfo { gr := this.getRoomByRoomIdAndRuleName(roomId, gameId, ruleName) if gr == nil { return nil } return &gr.GameRoomInfo /* if gr == nil { var ok bool ok, gr = this.createGameRoom(roomId, gameId, ruleName) if !ok { log.Release("gamemgr.getGameRoomInfo createGameRoom failed") return nil } } */ } // 开始小游戏 func (this *gamemgr) startGame(userId, roomId, gameId, playType, scene int, messageContent string, roomOwner int) (retCode int, message string, result int) { // 游戏信息 g := config.Mgr.GetGameInfo(gameId) if g.Id <= 0 { retCode, message = 11, "Fail, gameId is invalid" return } var ( isExist bool items []item.ItemPack ) // 玩法 for _, v := range g.Plays { if v.PlayType != playType { continue } // 判断数值范围是否有效 if len(v.Numbers) < 2 { log.Error("gamemgr.StartGame config error, userId=%d roomId=%d gameId=%d playType=%d scene=%d", userId, roomId, gameId, playType, scene) retCode, message = 14, "Fail, config.Numbers is invalid" return } // 取值范围 min, max := v.Numbers[0], v.Numbers[1] // 小-->大进行赋值 if min > max { min, max = max, min } // 生成随机结果 result = rand.Intn(max-min) + min // 场景 for _, s := range v.Fees { if s.Scene != scene { continue } // 存在 isExist = true // 费用 items = append(items, s.Items...) break } break } // 没有找到配置信息 if !isExist { retCode, message = 12, "Fail, no information found" return } // 扣除道具 if items != nil { if success := item.ConsumeBulk(userId, items, common.LOGTYPE_AUDIOROOM_GAME_START); !success { retCode, message = 13, "Insufficient expense deduction failed" return } } // 给房主加分成 for _, v := range items { income.TriggerGameIncome(userId, pb.TriggerData{ RoomId: roomId, GameId: gameId, RoomOwner: roomOwner, ItemType: v.ItemId, Amount: v.Count, }) } // 替换数字 messageContent = fmt.Sprintf(messageContent, result, playType) // 发送广播 go zego.SendBroadcastMessage(strconv.Itoa(roomId), strconv.Itoa(-userId), zego.MessageCategory_Chat, messageContent) // 成功 retCode, message = 1, "success" return } func (this *gamemgr) closeGameRoom(roomId, roomNo int) bool { r := this.getRoom(roomNo) if r == nil { log.Release("gamemgr.closeGameRoom roomId[%d] roomNo[%d] not found", roomId, roomNo) return false } if r.RoomId != roomId { log.Release("gamemgr.closeGameRoom roomId[%d] roomNo[%d] not equal to r.RoomId[%d]", roomId, roomNo, r.RoomId) return false } if !privateroom.ClosePrivateRoom(roomNo) { log.Release("gamemgr.closeGameRoom roomId[%d] roomNo[%d] ClosePrivateRoom failed", roomId, roomNo) return false } this.removeRoom(r) return true } func (this *gamemgr) removeRoom(room *gameRoom) { log.Debug("gamemgr.removeRoom %d", room.RoomNo) this.lockRemovedRooms.Lock() this.removedRooms[room.RoomNo] = &removedGameRoom{gameRoom: *room, removeTime: time.Now().Unix()} this.lockRemovedRooms.Unlock() this.lock.Lock() delete(this.roomlist, room.RoomNo) this.lock.Unlock() } func (this *gamemgr) getRemovedRoom(roomNo int) *gameRoom { this.lockRemovedRooms.RLock() gr, ok := this.removedRooms[roomNo] this.lockRemovedRooms.RUnlock() if !ok { return nil } return &gr.gameRoom } func (this *gamemgr) removeRoomsByGameRule(gameId int, gameRule string) { var toRemove []*gameRoom this.lock.RLock() for k, v := range this.roomlist { if v.GameId == gameId && v.RuleName == gameRule { toRemove = append(toRemove, this.roomlist[k]) } } this.lock.RUnlock() if len(toRemove) == 0 { return } if this.onRoomEnd != nil { for _, r := range toRemove { this.onRoomEnd(r.RoomId, r.RoomNo) } } this.lock.Lock() for _, r := range toRemove { delete(this.roomlist, r.RoomNo) } this.lock.Unlock() } func (this *gamemgr) checkRemovedRoom() { time.AfterFunc(10*time.Minute, this.checkRemovedRoom) var toRemove []int now := time.Now().Unix() this.lockRemovedRooms.RLock() for _, v := range this.removedRooms { if now-v.removeTime >= 600 { toRemove = append(toRemove, v.RoomNo) } } this.lockRemovedRooms.RUnlock() if len(toRemove) == 0 { return } log.Debug("gamemgr.checkRemovedRoom removing %v", toRemove) this.lockRemovedRooms.Lock() for _, v := range toRemove { delete(this.removedRooms, v) } this.lockRemovedRooms.Unlock() }