package frame import ( "encoding/json" "fmt" "math/rand" "sync" "time" "bet24.com/log" "bet24.com/servers/insecureframe/gate" audioroom "bet24.com/servers/micros/audioroom/proto" privateroom "bet24.com/servers/micros/privateroom/proto" "bet24.com/servers/user" "bet24.com/utils" ) type WatchUser struct { UserIndex int32 ChairId int UserId int } type ThreadsafeTable struct { tableSink TableSink tableSink_privateRoom TableSink_PrivateRoom users []int32 tableId int chairCount int tableStatus int multiSetStatus int privateData string watchUsers []WatchUser startTime int64 timers map[int]*time.Timer stopChan chan int timerChan chan int messageChan chan *threadsafe_message timerLock *sync.RWMutex owner int // 桌主,如果大于0表示私人房间,桌主离开后房间解散 roomNo int dismissRequest []int dismissTimer *time.Timer stopped bool createTime int64 tmpUserBets []audioroom.UserBet reportUserBetsTimer *time.Timer } func newThreadSafeTable(tableId int, owner int) *ThreadsafeTable { t := new(ThreadsafeTable) t.tableId = tableId t.timers = make(map[int]*time.Timer) t.stopChan = make(chan int) t.timerChan = make(chan int) t.timerLock = &sync.RWMutex{} t.messageChan = make(chan *threadsafe_message) t.owner = owner t.createTime = time.Now().Unix() return t } func (t *ThreadsafeTable) setRoomNo(roomNo int) { t.roomNo = roomNo } func (t *ThreadsafeTable) isPrivate() bool { return t.owner != 0 } func (t *ThreadsafeTable) isMatch() bool { return t.owner == -1 } func (t *ThreadsafeTable) setTableSink(sink TableSink) { t.tableSink = sink if t.isPrivate() { mid := (sink.(interface{})).(TableSink_PrivateRoom) t.tableSink_privateRoom = mid t.dismissRequest = make([]int, t.tableSink.OnGetChairCount()) } t.clear() go t.mainLoop() } func (t *ThreadsafeTable) clear() { t.chairCount = t.tableSink.OnGetChairCount() t.users = make([]int32, t.chairCount) for i := 0; i < t.chairCount; i++ { t.users[i] = -1 } } // 主线程 func (t *ThreadsafeTable) mainLoop() { log.Debug("newThreadSafeTable tableId[%d] running", t.tableId) for { select { case <-t.stopChan: log.Debug("ThreadsafeTable tableId[%d] exiting mainLoop", t.tableId) t.tableSink.Destroy() t.killAllTimer() if t.isPrivate() { privateroom.ClosePrivateRoom(t.roomNo) t.roomNo = 0 } return case timerId := <-t.timerChan: start := time.Now() t.tableSink.OnTimer(timerId) tc := time.Since(start) if tc.Milliseconds() >= 200 { log.Release("ThreadsafeTable.handleTimer[%d] run cost %v", timerId, tc) } case msg := <-t.messageChan: t.handleMsg(msg.Msg, msg.Data, msg.Callback) } } } func (t *ThreadsafeTable) handleMsg(msg, data string, callback chan byte) { if callback != nil { defer func() { callback <- 1 }() } defer utils.TimeCost(fmt.Sprintf("ThreadsafeTable.handleMsg[%s],%s", msg, data))() switch msg { case "AddUser": var addUser msg_AddUser err := json.Unmarshal([]byte(data), &addUser) if err != nil { log.Debug("ThreadsafeTable.handleMsg %s error %v", msg, err) return } t.AddUser_safe(addUser.UserIndex, addUser.ChairId, addUser.Replay, addUser.ToWatch) case "RemoveUser": var removeUser msg_RemoveUser err := json.Unmarshal([]byte(data), &removeUser) if err != nil { log.Debug("ThreadsafeTable.handleMsg %s error %v", msg, err) return } t.RemoveUser_safe(removeUser.UserIndex, removeUser.ToWatch, removeUser.ChangeTable) case "dismiss": t.dismiss_safe() case "userReplay": var replay msg_UserReplay err := json.Unmarshal([]byte(data), &replay) if err != nil { log.Debug("ThreadsafeTable.handleMsg %s error %v", msg, err) return } t.userReplay_safe(replay.OldUserIndex, replay.NewUserIndex) case "dump": t.dump_safe() case "dumpScene": t.dumpScene_safe() case "dumpTimers": t.dumpTimers_safe() case "TableMessage": var tm msg_TableMessage err := json.Unmarshal([]byte(data), &tm) if err != nil { log.Debug("ThreadsafeTable.handleMsg %s error %v", msg, err) return } t.onTableMessage_safe(tm.UserIndex, tm.Msg, tm.Data) case "UserWatch": var watch msg_UserIndex err := json.Unmarshal([]byte(data), &watch) if err != nil { log.Debug("ThreadsafeTable.handleMsg %s error %v", msg, err) return } t.UserWatch(watch.UserIndex) case "SetUserReadyStatus": var ready msg_SetReadyStatus err := json.Unmarshal([]byte(data), &ready) if err != nil { log.Debug("ThreadsafeTable.handleMsg %s error %v", msg, err) return } t.SetUserReadyStatus(ready.UserIndex, ready.IsReady) case "SetBaseScore": var baseScore msg_SetBaseScore err := json.Unmarshal([]byte(data), &baseScore) if err != nil { log.Debug("ThreadsafeTable.handleMsg %s error %v", msg, err) return } t.setBaseScore_safe(baseScore.BaseScore) case "privateRoomStatusChanged": var statusChanged msg_statusChanged err := json.Unmarshal([]byte(data), &statusChanged) if err != nil { log.Debug("ThreadsafeTable.handleMsg %s error %v", msg, err) return } t.privateRoomStatusChanged_safe(statusChanged.OldStatus, statusChanged.NewStatus) case "privateRoomDismissed": t.privateRoomDismissed_safe() default: log.Debug("ThreadsafeTable.handleMsg unhandled %s", msg) } } func (t *ThreadsafeTable) getWatcherCount() int { return len(t.watchUsers) } func (t *ThreadsafeTable) getPlayerCount() int { count := 0 for i := 0; i < t.chairCount; i++ { if t.users[i] != -1 { count++ } } return count } func (t *ThreadsafeTable) getUserCount() int { return t.getPlayerCount() + t.getWatcherCount() } func (t *ThreadsafeTable) getEmptyChair() int { if t.tableSink.IsDual() { for i := 0; i < 3; i += 2 { if t.users[i] == -1 { return i } } return -1 } // 随机分配 start := rand.Intn(t.chairCount) for i := 0; i < t.chairCount; i++ { chair := (start + i) % t.chairCount if t.users[chair] == -1 { return chair } } return -1 } func (t *ThreadsafeTable) isChairEmpty(chairId int) bool { if chairId < 0 || chairId >= t.chairCount { return false } if t.tableSink.IsDual() && chairId != 0 && chairId != 2 { log.Debug("ThreadsafeTable.isChairEmpty isdual and %d", chairId) return false } return t.users[chairId] == -1 } func (t *ThreadsafeTable) isValidChair(chairId int) bool { return chairId >= 0 && chairId < t.tableSink.OnGetChairCount() } func (t *ThreadsafeTable) removeAllWatchUsers() { for _, v := range t.watchUsers { go gameFrame.setUserStatus(v.UserIndex, user.UserStatus_Free) } t.watchUsers = []WatchUser{} } func (t *ThreadsafeTable) getStartTime() int64 { if t.tableStatus != TableStatus_Playing { return 0 } return time.Now().Unix() - t.startTime } func (t *ThreadsafeTable) isIdled() bool { return t.getStartTime() >= 300 } func (t *ThreadsafeTable) isAllRobot() bool { return t.tableSink.IsAllRobot() } func (t *ThreadsafeTable) isFull() bool { if t.tableSink.IsDual() { for i := 0; i < 3; i += 2 { if t.users[i] == -1 { return false } } return true } for i := 0; i < t.chairCount; i++ { if t.users[i] == -1 { return false } } return true } func (t *ThreadsafeTable) isSameRoom(data string) bool { if len(data) == 0 { return true } return data == t.privateData } func (t *ThreadsafeTable) checkSameIp(userIp string) bool { // 百人场不限制 if gameFrame.gameSink.GetChairCount() < 2 { return false } if t.ignoreSameIP() { return false } // 机器人不限制 if userIp == "" || userIp == "127.0.0.1" { return false } // 2人场 不限制 if t.tableSink.IsDual() { return false } for i := 0; i < t.chairCount; i++ { if t.users[i] == -1 { continue } usr := gate.GetUserInfo(t.users[i]) if usr == nil { continue } if usr.GetUserIp() == userIp { return true } } // 如果有旁观也挡一下 for _, v := range t.watchUsers { usr := gate.GetUserInfo(v.UserIndex) if usr == nil { continue } if usr.GetUserIp() == userIp { return true } } return false } func (t *ThreadsafeTable) canRemoveOneRobot() bool { // 这个函数有锁死风险 if true { return false } if t.tableSink.IsDual() { for i := 0; i < 3; i += 2 { if t.users[i] == -1 { return false } usr := gate.GetUserInfo(t.users[i]) if usr == nil { t.users[i] = -1 return false } if usr.IsRobot() { t.RemoveUser(t.users[i], false, false) return true } } return false } for i := 0; i < t.chairCount; i++ { if t.users[i] == -1 { return false } usr := gate.GetUserInfo(t.users[i]) if usr == nil { t.users[i] = -1 return false } if usr.IsRobot() { t.RemoveUser(t.users[i], false, false) return true } } return false } func (t *ThreadsafeTable) adjustPrivateChairId(chairId int) int { if !t.isPrivate() { return chairId } if t.tableSink.OnGetChairCount() == 4 && t.tableSink.IsDual() && chairId == 1 { return 2 } return chairId }