311 lines
5.5 KiB
Go
311 lines
5.5 KiB
Go
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)
|
|
}
|