package game import ( "encoding/json" "fmt" "log" "microGame/app/usercenter/cmd/api/internal/types" "microGame/pkg/common" "microGame/pkg/result" "sync" "time" ) // Hub maintains the set of active Players and broadcasts messages to the Players. type Hub struct { Rooms map[int64]*Room // 保存所有连接实例 Players map[int64]*Player // 保存所有连接实例 Broadcast chan []byte // 消息存放在这里 Register chan *Player Unregister chan *Player Mutex sync.Mutex // 并发安全锁 } func NewHub() *Hub { return &Hub{ Broadcast: make(chan []byte), Register: make(chan *Player), Unregister: make(chan *Player), Rooms: make(map[int64]*Room), Players: make(map[int64]*Player), } } func (r *Hub) Run() { // 启动调度任务 //go r.GoCronRoom(3 * time.Second) for { select { case player := <-r.Register: r.handleRegister(player) case player := <-r.Unregister: r.handleUnregister(player) case message := <-r.Broadcast: r.handleBroadcast(message) } } } // 处理用户注册逻辑 此处仅仅记录所有的长连接用户 func (r *Hub) handleRegister(o *Player) { if _, ok := r.Players[o.Uid]; !ok { r.Players[o.Uid] = o } o.WriteMsg(result.WSSuccess("register", types.MSG_SUCCESS)) fmt.Printf("Player %d registered in Players", o.Uid) } // 处理用户注销逻辑 func (r *Hub) handleUnregister(p *Player) { if r.removePlayerFromRoom(p.Uid, p.RoomId) { close(p.Send) close(p.actions) } r.tellUserLeave(p) fmt.Printf("Player %d unregistered from Room %d\n", p.Uid, p.RoomId) } // 处理消息广播逻辑 func (r *Hub) handleBroadcast(message []byte) { d := &types.WSMessage{} if err := json.Unmarshal(message, d); err != nil { fmt.Printf("Failed to unmarshal message: %v\n", err) return } switch d.Type { case "join": //判断游戏是否开始,如果游戏已经开始 用户进入观察者组;如果没开始 用户直接进入room room, code := r.addPlayerToRoom(d) if code == 200 { room.startGame() } case "leave": r.removePlayerFromRoom(d.Uid, d.Rid) log.Printf("玩家/观察者 %d 退出", d.Uid) case "destroy": r.destroyRoom(d.Rid) log.Printf("玩家/观察者 %d 退出", d.Uid) case "bet": room, exists := r.Rooms[d.Rid] if exists { room.bet(d, r.Players[d.Uid]) } log.Printf("玩家下注 %d 退出", d.Uid) case "sitDown": room, exists := r.Rooms[d.Rid] if exists { room.sitDown(d, r.Players[d.Uid]) } case "standUp": room, exists := r.Rooms[d.Rid] if exists { room.standUp(d, r.Players[d.Uid]) } default: log.Println("未知消息类型:", d.Type) } } // 将玩家添加到房间 func (r *Hub) addPlayerToRoom(d *types.WSMessage) (*Room, int) { r.Mutex.Lock() o, ok := r.Players[d.Uid] if !ok { r.Mutex.Unlock() log.Printf("观察者 %s 加入房间", o.Username) return nil, -1000 } room, exists := r.Rooms[d.Rid] if !exists { room = NewRoom(9, SB, BB, CHIPS, Timeout) r.Rooms[d.Rid] = room } r.Mutex.Unlock() //加入房间之前先判断房间是否已经满了 if room.Len() >= room.Cap() { o.WriteMsg(result.WSError("register", types.MSG_ROOM_FULL, -1001)) return nil, -1001 } joinRoom := &types.JoinRoom{Uid: d.Uid, RoomNumber: r.Rooms[d.Rid].RoomNumber, RoomPwd: ""} res := room.joinRoom(joinRoom, o) if !res { return nil, -1002 } return room, 200 } // 从房间移除玩家 func (r *Hub) removePlayerFromRoom(uid int64, rid int64) bool { r.Mutex.Lock() defer r.Mutex.Unlock() p, ok := r.Players[uid] if !ok { r.Mutex.Unlock() log.Printf("从房间移除玩家 %s", p.Username) return false } room, exists := r.Rooms[rid] if !exists { return false } p.RoomId = 0 room.removePlayer(p) room.removeObserve(p) return true } // CreateRoom 创建房间 func (r *Hub) CreateRoom(uid int64) *Room { r.Mutex.Lock() defer r.Mutex.Unlock() p, ok := r.Players[uid] if !ok { return nil } room, exists := r.Rooms[p.RoomId] if !exists { room = NewRoom(9, SB, BB, CHIPS, Timeout) r.Rooms[p.RoomId] = room p.RoomId = room.Rid fmt.Printf("房间 %d 创建成功\n", p.RoomId) return room } fmt.Printf("房间 %d 已存在\n", p.RoomId) go room.startGame() return room } // GoCronRoom 启动房间 Goroutine func (r *Hub) GoCronRoom(tickerInterval time.Duration) { ticker := time.NewTicker(tickerInterval) defer ticker.Stop() for { select { case <-ticker.C: r.checkAndStartRooms() } } } func (r *Hub) checkAndStartRooms() { r.Mutex.Lock() rooms := make(map[int64]*Room, len(r.Rooms)) for rid, v := range r.Rooms { rooms[rid] = v } r.Mutex.Unlock() for rid, room := range rooms { if room == nil || room.isEmpty() { r.destroyRoom(rid) // 清理无效或空房间 continue } // 防止重复启动 if room.isRunning { continue } // 标记房间正在运行 room.isRunning = true go common.RunWithRecover(func() { room.startGame() }) } } // **destroyRoom**: 房间销毁时调用 func (r *Hub) destroyRoom(roomID int64) { //如果房间中游戏正在进行中 不允许销毁房间 r.Mutex.Lock() room, exists := r.Rooms[roomID] if !exists { r.Mutex.Unlock() return } if room.status == 1 { r.Mutex.Unlock() return } // 关闭房间,通知 Goroutine 退出 room.CloseRoomChannel() delete(r.Rooms, roomID) // 从 Hub 中删除 r.Mutex.Unlock() log.Printf("房间 %d 已销毁\n", roomID) } // GetRoom 获取房间 func (r *Hub) GetRoom(roomID int64) *Room { if room, exists := r.Rooms[roomID]; exists { return room } return nil } // DeleteRoom 删除房间 func (r *Hub) DeleteRoom(roomID int64) { r.Mutex.Lock() defer r.Mutex.Unlock() delete(r.Rooms, roomID) } // 用户离开 广播给所有其他会员用户离开 func (r *Hub) tellUserLeave(o *Player) { r.Mutex.Lock() room, exists := r.Rooms[o.RoomId] r.Mutex.Unlock() // 提前释放锁,避免阻塞 if !exists { o.WriteMsg(result.WSSuccess("register", types.MSG_NOT_IN_ROOM)) return } // 构造广播消息 msg := &types.WSMessage{ Type: "leave", Data: "", Uid: 100, Rid: 100, } // 发送广播 room.Broadcast(result.WSSuccess("leave", msg), true, 100) } //func (r *Hub) getMapKeys(m map[int64]*Player) []int64 { // keys := make([]int64, 0, len(m)) // for k := range m { // keys = append(keys, k) // } // return keys //}