package handler import ( "encoding/json" "fmt" "math/rand" "os" "strconv" "sync" "time" proto "bet24.com/servers/micros/privateroom/proto" "bet24.com/log" "bet24.com/servers/common" cash "bet24.com/servers/micros/money/proto" platformconfig "bet24.com/servers/micros/platformconfig/proto" db2 "bet24.com/servers/micros/privateroom/config" game_client "bet24.com/servers/micros/privateroom/game/client" pb "bet24.com/servers/micros/privateroom/proto" rule_status "bet24.com/servers/micros/privateroom/status/client" ) const ( min_room_no = 100000 max_room_no = 999999 room_timeout_playing = 7200 room_timeout_not_playing = 600 room_timeout_ended = 60 ) const config_key = "privateroom_config" const refresh_room_config_sec = 600 //const rooms_temp_key = "privateroom_rooms_temp" type roomManager struct { lock *sync.RWMutex roomNumbers []int32 roomIndex int rooms map[int]*roomInfo TaxRate int Timeout_Play int64 Timeout_Free int64 Timeout_End int64 recvMgr *receiverMgr pubishRoomInfoFlag bool } var mgr *roomManager func getRoomManager() *roomManager { if mgr == nil { mgr = new(roomManager) //mgr.ctor() } return mgr } func (rm *roomManager) run() { rm.lock = &sync.RWMutex{} rm.rooms = make(map[int]*roomInfo) rm.loadConfig() log.Release("roomManager.run taxRate = %d", rm.TaxRate) count := max_room_no - min_room_no + 1 rm.roomNumbers = make([]int32, count) for i := int32(min_room_no); i < int32(max_room_no); i++ { rm.roomNumbers[i-min_room_no] = i } for i := count - 1; i > 1; i-- { rep := rand.Intn(i) rm.roomNumbers[i], rm.roomNumbers[rep] = rm.roomNumbers[rep], rm.roomNumbers[i] } rm.roomIndex = 0 rm.recvMgr = newReceiverMgr() go rm.checkTimeout() } func (rm *roomManager) loadConfig() { defer func() { time.AfterFunc(refresh_room_config_sec*time.Second, rm.loadConfig) }() configString := platformconfig.GetConfig(config_key) if configString == "" { data, err := os.ReadFile("fishconf/privateroom.json") if err != nil { log.Release("privateroom.roommanager.loadData read json failed") return } configString = string(data) platformconfig.SetConfig(config_key, configString) } err := json.Unmarshal([]byte(configString), rm) if err != nil { log.Release("privateroom.roommanager.loadData Unmarshal failed err:%v", err) return } if rm.Timeout_End == 0 { rm.Timeout_End = room_timeout_ended } if rm.Timeout_Play == 0 { rm.Timeout_Play = room_timeout_playing } if rm.Timeout_Free == 0 { rm.Timeout_Free = room_timeout_not_playing } } func (rm *roomManager) checkTimeout() { time.AfterFunc(30*time.Second, rm.checkTimeout) var toRemove []int var toDelayRemove []*roomInfo rm.lock.RLock() for _, v := range rm.rooms { t := v.isTimeout(rm.Timeout_Free, rm.Timeout_Play, rm.Timeout_End) if t == room_timeout { toRemove = append(toRemove, v.RoomNo) } else if t == room_timeout_empty { toDelayRemove = append(toDelayRemove, v) } } rm.lock.RUnlock() if len(toRemove) > 0 { log.Debug("roomManager.checkTimeout removing rooms %v", toRemove) for _, v := range toRemove { rm.closeRoom(v, "timeout") } } if len(toDelayRemove) > 0 { log.Debug("roomManager.checkTimeout delay removing rooms %d", len(toDelayRemove)) for _, v := range toDelayRemove { rm.delayCloseRoom(v, "timeout") } } } func (rm *roomManager) getRoomNumber() int { rm.lock.Lock() defer rm.lock.Unlock() ret := int(rm.roomNumbers[rm.roomIndex]) rm.roomIndex = (rm.roomIndex + 1) % len(rm.roomNumbers) return ret } func (rm *roomManager) addRoom(userId int, gameId int, gameName, serverAddr string, tableId int) (int, string) { errMsg := "ok" roomNo := 0 roomNo = rm.getRoomNumber() rm.lock.Lock() rm.rooms[roomNo] = newRoomInfo(roomNo, serverAddr, tableId, userId, gameId, gameName) // room := proto.GetIdleRoom2(1,tableId) // roominfo.Room = room rm.lock.Unlock() return roomNo, errMsg } func (rm *roomManager) delayCloseRoom(r *roomInfo, reason string) { log.Debug("roommanager.delayCloseRoom %d,%s", r.RoomNo, reason) if r.Status == pb.PrivateRoomStatus_Ended || r.isMatch() { log.Debug("roommanager.delayCloseRoom %d,%s doCloseRoom", r.RoomNo, reason) rm.doCloseRoom(r, reason) return } roomNo := r.RoomNo delaySecs := 5 time.AfterFunc(time.Duration(delaySecs)*time.Second, func() { if !r.isEmpty() { log.Release("roomManager.delayCloseRoom failed roomNo[%d] not empty", r.RoomNo) return } log.Debug("roommanager.delayCloseRoom after [%d] secondss %d,%s", delaySecs, roomNo, reason) rm.closeRoom(roomNo, reason) }) } func (rm *roomManager) doCloseRoom(r *roomInfo, reason string) bool { roomNo := r.RoomNo if r.Status < pb.PrivateRoomStatus_Playing { // 如果有玩家,也退费 for _, v := range r.UserList { if v == nil { continue } if r.Fee > 0 { // cash.GiveMoney(v.UserId, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "fee return", "") cash.GiveMoney2(v.UserId, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "fee return", "",v.YyfUid) } } // 可以关闭,这里应该有退费之类的操作 if r.createFee > 0 { //cash.GiveMoney(r.Owner, r.createFee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "create return", "") for _, v := range r.UserList { if v.UserId==r.Owner{ //cash.GiveMoney2(r.Owner, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "fee return", "",r.Owner.yyfUid) cash.GiveMoney2(v.UserId, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "fee return", "",v.YyfUid) } } } } if reason == "timeout" { // 超时异常结束,通知一下比赛模块 var winners []int winner := r.getAUserId() if winner != 0 { winners = append(winners, winner) r.setWinners(winners) } } // 如果游戏本来就结束了 if r.Status == pb.PrivateRoomStatus_Ended { reason = "Normal Ended" } r.Status = pb.PrivateRoomStatus_Ended //if addToHistory { //getHistoryManager().addHistory(*r, reason) //} go game_client.OnRoomDismissed(r.service_addr, r.RoomNo) log.Debug("closeRoom %d %s", roomNo, reason) rm.lock.Lock() var gameRoom proto.GameRoom= proto.GameRoom{TableId:rm.rooms[roomNo].TableId} //var gameRoom db2.Engine.Get(&gameRoom) gameRoom.PlayerNum=0 gameRoom.Status=1 fmt.Printf("游戏结束更新房间") db2.Engine.ID(gameRoom.Id).Cols("player_num","status").Update(gameRoom) delete(rm.rooms, roomNo) rm.lock.Unlock() getSubsManager().publishRoomClosed(roomNo) getCallManager().removeRoomCall(roomNo) return true } func (rm *roomManager) closeRoom(roomNo int, reason string) bool { if roomNo == 0 { //log.Debug("%s", debug.Stack()) return false } r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.closeRoom failed roomNo[%d] not exist [%s]", roomNo, reason) return false } return rm.doCloseRoom(r, reason) } func (rm *roomManager) setRoomStatus(roomNo, status int) { r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.setRoomStatus failed roomNo[%d] not exist", roomNo) return } // 通知服务器 oldStatus := r.Status r.Status = status if oldStatus != pb.PrivateRoomStatus_Playing && status == pb.PrivateRoomStatus_Playing { r.roomStart() } if oldStatus == pb.PrivateRoomStatus_Playing && status == pb.PrivateRoomStatus_Ended { r.endedTime = time.Now().Unix() getHistoryManager().addHistory(*r, "nomal end") } rm.postRoomStatusChanged(roomNo, oldStatus, r.Status) go game_client.OnRoomStatusChanged(r.service_addr, r.RoomNo, oldStatus, r.Status) getSubsManager().publishRoomInfo(r.RoomInfo) if status >= pb.PrivateRoomStatus_Playing { getCallManager().removeRoomCall(roomNo) } } func (rm *roomManager) getRoomStatus(roomNo int) int { r := rm.getRoomInfo(roomNo) if r == nil { return pb.PrivateRoomStatus_Invalid } return r.Status } func (rm *roomManager) getRoomInfo(roomNo int) *roomInfo { rm.lock.RLock() defer rm.lock.RUnlock() ret, ok := rm.rooms[roomNo] if ok { return ret } return nil } func (rm *roomManager) getUserRooms(userId int) string { var ret []roomInfo rm.lock.RLock() for _, v := range rm.rooms { if v.Owner == userId { ret = append(ret, *v) } } rm.lock.RUnlock() d, _ := json.Marshal(ret) return string(d) } func (rm *roomManager) getRoomsByGameId(gameId int, userId int) string { var ret []roomInfo rm.lock.RLock() for _, v := range rm.rooms { if !v.IsPublic && v.Owner != userId { continue } // 房间已结束 if v.Status >= pb.PrivateRoomStatus_Ended { continue } if v.GameId == gameId || gameId == 0 { ret = append(ret, *v) } } rm.lock.RUnlock() d, _ := json.Marshal(ret) return string(d) } func (rm *roomManager) getPlayingRoomNo(userId int) string { ret := "" rm.lock.RLock() for _, v := range rm.rooms { if v.isUserPlaying(userId) { ret = fmt.Sprintf("%d", v.RoomNo) break } } rm.lock.RUnlock() return ret } func (rm *roomManager) setRoomExtra(roomNo int, ruleName string, ruleData string, userCount int, target int, fee int, serviceAddr string, createFee int, isDual bool, prize int, roomType string, isPublic bool, playTime int, extraInfo string) { r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.setRoomExtra roomNo[%d] not found", roomNo) return } r.Status = pb.PrivateRoomStatus_Free r.createFee = createFee if r.createFee <= 0 { r.Prize = fee * userCount * (100 - rm.TaxRate) / 100 } else { r.Prize = prize } r.RoomType = roomType r.IsPublic = isPublic r.setExtra(ruleName, ruleData, userCount, fee, target, serviceAddr, isDual, playTime) r.ExtraInfo = extraInfo getSubsManager().publishRoomCreated(roomNo) } func (rm *roomManager) userRequestSit(roomNo int, userId int, nickName string, faceId int, faceUrl string, chairId int, score, baseScore, setCount int,yyfUid int) string { var ret struct { ErrMsg string ServerAddr string TableId int ChairId int BlackUsers []int // 桌子内有黑名单玩家 } log.Debug("roomManager.userRequestSit[%d,%d] score[%d] baseScore[%d]", roomNo, userId, score, baseScore) r := rm.getRoomInfo(roomNo) for { if r == nil { log.Release("roomManager.userRequestSit roomNo[%d] not found", roomNo) ret.ErrMsg = fmt.Sprintf("Room [%d] not found", roomNo) break } // 如果我已经在里面了 oldChairId := r.getUserChair(userId) if oldChairId >= 0 { ret.ServerAddr = r.ServerAddr ret.TableId = r.TableId ret.ChairId = oldChairId break } // 扣钱 if r.Fee > 0 && !cash.ReduceMoney2(userId, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "enter", "",yyfUid) { // if r.Fee > 0 && !cash.ReduceMoney(userId, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "enter", "") { log.Release("roomManager.userRequestSit not enough cash or fee[%d]", r.Fee) ret.ErrMsg = fmt.Sprintf("not enough cash or fee[%d]", r.Fee) break } ret.ChairId, ret.ErrMsg, ret.BlackUsers = r.userRequestSit(userId, nickName, faceId, faceUrl, chairId, score, baseScore, setCount,yyfUid) if ret.ChairId != -1 { ret.ServerAddr = r.ServerAddr ret.TableId = r.TableId } else { // 加回去 if r.Fee > 0 { //cash.GiveMoney(userId, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "enter return", "") cash.GiveMoney2(userId, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "enter return", "",yyfUid) } } break } d, _ := json.Marshal(ret) return string(d) } func (rm *roomManager) userLeave(roomNo, userId int) bool { r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.userLeave roomNo[%d] not found", roomNo) return false } if r.Status == pb.PrivateRoomStatus_Playing { log.Release("roomManager.userLeave room has started") return false } // 如果是比赛场,先不让离开 if r.isMatch() && r.Status < pb.PrivateRoomStatus_Playing { log.Release("roomManager.userLeave matchroom not end") return false } return rm.removeUser(r, userId) } func (rm *roomManager) removeUser(r *roomInfo, userId int) bool { ret := r.removeUser(userId) if !ret { return ret } if r.Fee > 0 && r.Status < pb.PrivateRoomStatus_Playing { for _, v := range r.UserList { if v.UserId==userId{ // cash.GiveMoney(userId, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "enter return", "") cash.GiveMoney2(v.UserId, r.Fee, common.LOGTYPE_PRIVATEROOM_ENTER, "privateroom", "fee return", "",v.YyfUid) } } } // 如果是公共房间 if r.isEmpty() { go rm.delayCloseRoom(r, "empty") } else { getSubsManager().publishRoomInfo(r.RoomInfo) } return ret } func (rm *roomManager) userSit(roomNo, userId, chairId int) int { r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.userSit roomNo[%d] not found", roomNo) return 0 } ret := r.userSit(userId, chairId) if ret == 1 { getSubsManager().publishRoomInfo(r.RoomInfo) } return ret } func (rm *roomManager) changeChair(roomNo, userId, chairId int) bool { r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.changeChair roomNo[%d] not found", roomNo) return false } ret := r.changeChair(userId, chairId) if ret { getSubsManager().publishRoomInfo(r.RoomInfo) } return ret } func (rm *roomManager) updateUserScore(roomNo, userId, scoreDelta int) bool { r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.addUserScore roomNo[%d] not found", roomNo) return false } if r.updateUserScore(userId, scoreDelta) { rm.delayPublishRoomInfo(roomNo) return true } return false } func (rm *roomManager) delayPublishRoomInfo(roomNo int) { if rm.pubishRoomInfoFlag { // 已经做了延迟 return } rm.pubishRoomInfoFlag = true time.AfterFunc(time.Second, func() { if !rm.pubishRoomInfoFlag { return } rm.pubishRoomInfoFlag = false r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.delayPublishRoomInfo roomNo[%d] not found", roomNo) return } log.Debug("roomManager.delayPublishRoomInfo [%d]", roomNo) getSubsManager().publishRoomInfo(r.RoomInfo) }) } func (rm *roomManager) setWinners(roomNo int, winners []int) { log.Debug("privateroom.roomManager.setWinners %d,%v", roomNo, winners) r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.setWinners roomNo[%d] not found", roomNo) return } if !r.setWinners(winners) { log.Release("roomManager.setWinners roomNo[%d] setWinners failed", roomNo) return } // 正常结束,发奖金 if r.Prize > 0 { winners := r.getWinners() if len(winners) > 0 { prize := r.Prize / len(winners) tax := 0 //prize * rm.TaxRate / 100 prize -= tax for _, v := range winners { r.setPrizeAndTax(v, prize, tax) for _, u := range r.userList { if (u.UserId==v){ cash.ModifyMoneyWithTax2(v, prize, tax, common.LOGTYPE_PRIVATEROOM_PRIZE, "privateroom", "prize", "",u.YyfUid) // cash.ModifyMoneyWithTax(v, prize, tax, common.LOGTYPE_PRIVATEROOM_PRIZE, "privateroom", "prize", "") } } } } } else if r.Fee > 0 { totalPrize := r.UserCount * r.Fee winners := r.getWinners() if len(winners) > 0 { prize := totalPrize / len(winners) tax := prize * rm.TaxRate / 100 prize -= tax for _, v := range winners { r.setPrizeAndTax(v, prize, tax) for _, u := range r.userList { if (u.UserId==v){ cash.ModifyMoneyWithTax2(v, prize, tax, common.LOGTYPE_PRIVATEROOM_PRIZE, "privateroom", "prize", "",u.YyfUid) // cash.ModifyMoneyWithTax(v, prize, tax, common.LOGTYPE_PRIVATEROOM_PRIZE, "privateroom", "prize", "") } } } } } log.Debug("privateroom.roomManager.setWinners %d,%v dump", roomNo, winners) r.dump() } func (rm *roomManager) dump(param1 string) { log.Release("-------------------------------") log.Release("privateroom.roomManager.dump[%s]", param1) defer func() { log.Release("+++++++++++++++++++++++++++++++") log.Release("") }() if param1 == "" { rm.lock.RLock() for _, v := range rm.rooms { v.dump() } rm.lock.RUnlock() return } if param1 == "receiver" { rm.recvMgr.dump() return } roomNo, err := strconv.Atoi(param1) if err != nil { log.Release(" atoi error %v", err) return } room := rm.getRoomInfo(roomNo) if room == nil { log.Release(" room[%d] not exist", roomNo) return } public := "" if room.IsPublic { public = "Public" } log.Release(" RoomNo: %d %s", roomNo, public) log.Release(" Owner: %d", room.Owner) log.Release(" Idled: %d", room.getIdle()) log.Release(" Game: %s", room.GameName) log.Release(" Addr: %s", room.ServerAddr) log.Release(" TableId: %d", room.TableId) log.Release(" Rule: %s", room.RuleName) log.Release(" UserCount: %d", room.UserCount) log.Release(" Fee: %d", room.Fee) log.Release(" Prize: %d", room.Prize) log.Release(" Status: %d", room.Status) log.Release(" StartTime %s", room.StartTime) } func (rm *roomManager) flush() { } func (rm *roomManager) registerRoomStatus(addr string) { rm.recvMgr.setReceiver(addr, true) } func (rm *roomManager) forceUserEnter(userId int, roomNo int, chairId int, secAfter int) { r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.forceUserEnter failed roomNo[%d] not exist", roomNo) return } time.AfterFunc(time.Duration(secAfter)*time.Second, func() { game_client.ForceEnterUser(r.service_addr, userId, roomNo, chairId) }) } func (rm *roomManager) postRoomStart(roomNo int) { receivers := rm.recvMgr.getActiveReceivers() if len(receivers) == 0 { log.Release("roomManager.postRoomStart no receiver") return } for _, c := range receivers { ok := rule_status.OnRoomStatusInfo(c, rule_status.Roomstatus_start, roomNo, 0, 0, 0, 0, nil) if !ok { log.Debug("postRoomStart failed to send messsage to %s", c) rm.recvMgr.setReceiver(c, false) } } } func (rm *roomManager) postRoomEnd(roomNo int, winners []int) { receivers := rm.recvMgr.getActiveReceivers() if len(receivers) == 0 { log.Release("roomManager.postRoomEnd no receiver") return } for _, c := range receivers { ok := rule_status.OnRoomStatusInfo(c, rule_status.Roomstatus_end, roomNo, 0, 0, 0, 0, winners) if !ok { log.Debug("postRoomEnd failed to send messsage to %s", c) rm.recvMgr.setReceiver(c, false) } } } func (rm *roomManager) postRoomStatusChanged(roomNo int, old, new int) { receivers := rm.recvMgr.getActiveReceivers() if len(receivers) == 0 { log.Release("roomManager.postRoomStatusChanged no receiver") return } for _, c := range receivers { ok := rule_status.OnRoomStatusInfo(c, rule_status.Roomstatus_statuschanged, roomNo, 0, 0, old, new, nil) if !ok { log.Debug("postRoomStatusChanged failed to send messsage to %s", c) rm.recvMgr.setReceiver(c, false) } } } func (rm *roomManager) postUserScore(roomNo int, userId, scoreDelta int) { receivers := rm.recvMgr.getActiveReceivers() if len(receivers) == 0 { log.Release("roomManager.postUserScore no receiver") return } for _, c := range receivers { ok := rule_status.OnRoomStatusInfo(c, rule_status.Roomstatus_scorechanged, roomNo, userId, scoreDelta, 0, 0, nil) if !ok { log.Debug("postUserScore failed to send messsage to %s", c) rm.recvMgr.setReceiver(c, false) } } } func (rm *roomManager) getUserRoomInfo(userId, roomNo int) *pb.RoomUser { r := rm.getRoomInfo(roomNo) if r == nil { log.Release("roomManager.getUserRoomInfo failed [%d]", roomNo) return nil } return r.getUserInfo(userId) } func (rm *roomManager) removeRoomsByServerAddr(addr string) { if addr == "" { return } var toRemove []*roomInfo rm.lock.RLock() for k, v := range rm.rooms { if v.ServerAddr == addr { toRemove = append(toRemove, rm.rooms[k]) } } rm.lock.RUnlock() if len(toRemove) == 0 { return } for _, v := range toRemove { rm.postRoomEnd(v.RoomNo, nil) } rm.lock.Lock() for _, v := range toRemove { delete(rm.rooms, v.RoomNo) } rm.lock.Unlock() }