Files
Embalming_Girl_Server/room/manager.go
T
2026-06-18 17:31:10 +08:00

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)
}