package room import ( "fmt" "math/rand" "sync" "time" ) type Player struct { ID string Nickname string IsReady bool IsHost bool IsBot bool Send chan []byte } type Room struct { ID string State string MaxPlayers int HarmonyTarget int ChaosMode bool Players map[string]*Player PlayerOrder []string mu sync.RWMutex } type Manager struct { rooms map[string]*Room mu sync.RWMutex } func NewManager() *Manager { return &Manager{ rooms: make(map[string]*Room), } } func (m *Manager) CreateTestRoom(roomID string, playerCount int) *Room { m.mu.Lock() defer m.mu.Unlock() rm := &Room{ ID: roomID, State: "waiting", MaxPlayers: playerCount, Players: make(map[string]*Player), } botNames := []string{"Bot-A", "Bot-B", "Bot-C", "Bot-D", "Bot-E"} for i := 0; i < playerCount-1; i++ { botID := fmt.Sprintf("bot_%s_%d", roomID, i) bot := &Player{ ID: botID, Nickname: botNames[i%len(botNames)], IsReady: true, IsBot: true, Send: nil, } rm.Players[botID] = bot rm.PlayerOrder = append(rm.PlayerOrder, botID) } m.rooms[roomID] = rm return rm } func (m *Manager) CreateRoom(hostID, nickname string, maxPlayers int) *Room { m.mu.Lock() defer m.mu.Unlock() roomID := generateRoomID() room := &Room{ ID: roomID, State: "waiting", MaxPlayers: maxPlayers, Players: make(map[string]*Player), } host := &Player{ ID: hostID, Nickname: nickname, IsHost: true, Send: make(chan []byte, 256), } room.Players[hostID] = host room.PlayerOrder = append(room.PlayerOrder, hostID) m.rooms[roomID] = room return room } func (m *Manager) GetRoom(roomID string) *Room { m.mu.RLock() defer m.mu.RUnlock() return m.rooms[roomID] } func (m *Manager) ListRooms() []map[string]interface{} { m.mu.RLock() defer m.mu.RUnlock() var list []map[string]interface{} for _, rm := range m.rooms { rm.mu.RLock() realCount := 0 totalCount := len(rm.Players) for _, p := range rm.Players { if !p.IsBot { realCount++ } } rm.mu.RUnlock() list = append(list, map[string]interface{}{ "room_id": rm.ID, "state": rm.State, "player_count": totalCount, "real_players": realCount, "max_players": rm.MaxPlayers, }) } return list } func (m *Manager) JoinRoom(roomID, playerID, nickname string) (*Room, bool) { m.mu.RLock() room, exists := m.rooms[roomID] m.mu.RUnlock() if !exists { return nil, false } room.mu.Lock() defer room.mu.Unlock() if len(room.Players) >= room.MaxPlayers { return nil, false } if room.State != "waiting" { return nil, false } player := &Player{ ID: playerID, Nickname: nickname, Send: make(chan []byte, 256), } room.Players[playerID] = player room.PlayerOrder = append(room.PlayerOrder, playerID) hasRealHost := false for _, p := range room.Players { if p.IsHost && !p.IsBot { hasRealHost = true break } } if !hasRealHost { for _, p := range room.Players { p.IsHost = false } player.IsHost = true } return room, true } func (m *Manager) LeaveRoom(roomID, playerID string) { m.mu.Lock() defer m.mu.Unlock() room, exists := m.rooms[roomID] if !exists { return } room.mu.Lock() defer room.mu.Unlock() delete(room.Players, playerID) newOrder := make([]string, 0) for _, id := range room.PlayerOrder { if id != playerID { newOrder = append(newOrder, id) } } room.PlayerOrder = newOrder realCount := 0 for _, p := range room.Players { if !p.IsBot { realCount++ } } if realCount == 0 && !isTestRoom(roomID) { delete(m.rooms, roomID) return } if realCount == 0 && isTestRoom(roomID) { for _, p := range room.Players { p.IsHost = false } room.State = "waiting" return } hasHost := false for _, p := range room.Players { if p.IsHost && !p.IsBot { hasHost = true break } } if !hasHost { for _, p := range room.Players { p.IsHost = false } for _, id := range room.PlayerOrder { p := room.Players[id] if !p.IsBot { p.IsHost = true break } } } } func isTestRoom(roomID string) bool { return roomID == "TEST3" || roomID == "TEST4" || roomID == "TEST5" || roomID == "TEST6" } func (m *Manager) SetReady(roomID, playerID string, ready bool) bool { room := m.GetRoom(roomID) if room == nil { return false } room.mu.Lock() defer room.mu.Unlock() p, exists := room.Players[playerID] if !exists { return false } p.IsReady = ready return true } func (r *Room) GetPlayerInfos() []map[string]interface{} { r.mu.RLock() defer r.mu.RUnlock() var infos []map[string]interface{} for _, id := range r.PlayerOrder { p, ok := r.Players[id] if !ok { continue } infos = append(infos, map[string]interface{}{ "player_id": p.ID, "nickname": p.Nickname, "is_ready": p.IsReady, "is_host": p.IsHost, "is_bot": p.IsBot, }) } return infos } func (r *Room) BroadcastToReal(data []byte) { r.mu.RLock() defer r.mu.RUnlock() for _, p := range r.Players { if p.IsBot || p.Send == nil { continue } select { case p.Send <- data: default: } } } func (r *Room) SendToPlayer(playerID string, data []byte) { r.mu.RLock() defer r.mu.RUnlock() p, ok := r.Players[playerID] if !ok || p.IsBot || p.Send == nil { return } select { case p.Send <- data: default: } } func generateRoomID() string { src := rand.NewSource(time.Now().UnixNano()) r := rand.New(src) const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" b := make([]byte, 6) for i := range b { b[i] = charset[r.Intn(len(charset))] } return string(b) }