完善了Web管理端
This commit is contained in:
+202
-9
@@ -5,6 +5,7 @@ import (
|
||||
"Embalming_Girl_Server/game"
|
||||
"Embalming_Girl_Server/room"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
@@ -25,6 +26,7 @@ type Client struct {
|
||||
Username string
|
||||
Nickname string
|
||||
Token string
|
||||
Version string
|
||||
Conn *websocket.Conn
|
||||
Send chan []byte
|
||||
mu sync.Mutex
|
||||
@@ -51,6 +53,18 @@ func (h *Hub) OnlineCount() int {
|
||||
return len(h.clients)
|
||||
}
|
||||
|
||||
func (h *Hub) OnlineClientsList() []map[string]interface{} {
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
var list []map[string]interface{}
|
||||
for _, c := range h.clients {
|
||||
list = append(list, map[string]interface{}{
|
||||
"id": c.ID, "username": c.Username, "nickname": c.Nickname, "room_id": c.RoomID, "version": c.Version,
|
||||
})
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (h *Hub) InitTestRooms() {
|
||||
for _, n := range []int{3, 4, 5, 6} {
|
||||
rid := "TEST" + string(rune('0'+n))
|
||||
@@ -109,15 +123,59 @@ func (h *Hub) removeClient(c *Client) {
|
||||
h.mu.Lock()
|
||||
delete(h.clients, c.ID)
|
||||
h.mu.Unlock()
|
||||
|
||||
if c.RoomID != "" {
|
||||
h.RoomMgr.LeaveRoom(c.RoomID, c.ID)
|
||||
rm := h.RoomMgr.GetRoom(c.RoomID)
|
||||
if rm != nil {
|
||||
h.broadcastRoomState(rm)
|
||||
h.mu.RLock()
|
||||
_, hasGame := h.engines[c.RoomID]
|
||||
h.mu.RUnlock()
|
||||
|
||||
if hasGame && c.Username != "" {
|
||||
h.RoomMgr.DisconnectPlayer(c.RoomID, c.ID)
|
||||
h.broadcastToRoom(c.RoomID, "player_disconnected", map[string]interface{}{
|
||||
"nickname": c.Nickname, "message": c.Nickname + " 断线了",
|
||||
})
|
||||
log.Printf("Player %s disconnected, slot preserved, 60s takeover timer started", c.Username)
|
||||
|
||||
go func(roomID, playerID, nick string) {
|
||||
time.Sleep(60 * time.Second)
|
||||
if !h.RoomMgr.IsPlayerDisconnected(roomID, playerID) {
|
||||
return
|
||||
}
|
||||
h.RoomMgr.SetPlayerBotControlled(roomID, playerID, true)
|
||||
|
||||
h.mu.RLock()
|
||||
eng := h.engines[roomID]
|
||||
h.mu.RUnlock()
|
||||
if eng != nil {
|
||||
eng.SetBotControlled(playerID, true)
|
||||
}
|
||||
|
||||
h.broadcastToRoom(roomID, "player_bot_takeover", map[string]interface{}{
|
||||
"nickname": nick, "message": nick + " 已被人机接管",
|
||||
})
|
||||
log.Printf("Player %s taken over by bot in room %s", nick, roomID)
|
||||
|
||||
if eng != nil && eng.Phase == "playing" {
|
||||
cp := eng.CurrentPlayer()
|
||||
if cp != nil && cp.ID == playerID {
|
||||
h.processBotTurns(roomID)
|
||||
}
|
||||
}
|
||||
}(c.RoomID, c.ID, c.Nickname)
|
||||
} else {
|
||||
h.RoomMgr.LeaveRoom(c.RoomID, c.ID)
|
||||
rm := h.RoomMgr.GetRoom(c.RoomID)
|
||||
if rm != nil {
|
||||
h.broadcastRoomState(rm)
|
||||
} else {
|
||||
h.mu.Lock()
|
||||
delete(h.engines, c.RoomID)
|
||||
h.mu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
close(c.Send)
|
||||
log.Printf("Disconnected: %s", c.ID)
|
||||
log.Printf("Disconnected: %s (%s)", c.ID, c.Username)
|
||||
}
|
||||
|
||||
func (h *Hub) handleMessage(c *Client, msg map[string]interface{}) {
|
||||
@@ -136,6 +194,8 @@ func (h *Hub) handleMessage(c *Client, msg map[string]interface{}) {
|
||||
h.handleCreateRoom(c, p)
|
||||
case "join_room":
|
||||
h.handleJoinRoom(c, p)
|
||||
case "rejoin_room":
|
||||
h.handleRejoin(c, p)
|
||||
case "leave_room":
|
||||
h.handleLeaveRoom(c)
|
||||
case "set_ready":
|
||||
@@ -154,6 +214,8 @@ func (h *Hub) handleMessage(c *Client, msg map[string]interface{}) {
|
||||
h.handleChat(c, p)
|
||||
case "set_chaos":
|
||||
h.handleSetChaos(c, p)
|
||||
case "create_bot_room":
|
||||
h.handleCreateBotRoom(c, p)
|
||||
case "heartbeat":
|
||||
h.sendTo(c, "heartbeat_ack", nil)
|
||||
}
|
||||
@@ -203,12 +265,20 @@ func (h *Hub) handleLogin(c *Client, p map[string]interface{}) {
|
||||
c.Username = user.Username
|
||||
c.Nickname = user.Nickname
|
||||
c.Token = token
|
||||
if v, ok := p["version"].(string); ok {
|
||||
c.Version = v
|
||||
}
|
||||
log.Printf("User logged in: %s (%s)", user.Username, c.ID)
|
||||
|
||||
h.sendTo(c, "login_result", map[string]interface{}{
|
||||
result := map[string]interface{}{
|
||||
"success": true, "token": token,
|
||||
"username": user.Username, "nickname": user.Nickname, "avatar": user.Avatar, "bio": user.Bio,
|
||||
})
|
||||
}
|
||||
if roomID, playerID, found := h.RoomMgr.FindRoomByUsername(user.Username); found {
|
||||
result["active_room"] = roomID
|
||||
result["active_player_id"] = playerID
|
||||
}
|
||||
h.sendTo(c, "login_result", result)
|
||||
}
|
||||
|
||||
func (h *Hub) handleUpdateProfile(c *Client, p map[string]interface{}) {
|
||||
@@ -239,6 +309,41 @@ func (h *Hub) handleGetProfile(c *Client, p map[string]interface{}) {
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Hub) handleRejoin(c *Client, p map[string]interface{}) {
|
||||
roomID, _ := p["room_id"].(string)
|
||||
oldPID, _ := p["player_id"].(string)
|
||||
if roomID == "" || oldPID == "" {
|
||||
h.sendTo(c, "error", map[string]interface{}{"message": "缺少参数"})
|
||||
return
|
||||
}
|
||||
if !h.RoomMgr.ReconnectPlayer(roomID, oldPID, c.ID, c.Send) {
|
||||
h.sendTo(c, "error", map[string]interface{}{"message": "无法重连到对局"})
|
||||
return
|
||||
}
|
||||
c.RoomID = roomID
|
||||
log.Printf("Player %s rejoined room %s", c.Username, roomID)
|
||||
|
||||
h.mu.RLock()
|
||||
eng := h.engines[roomID]
|
||||
h.mu.RUnlock()
|
||||
|
||||
if eng != nil {
|
||||
eng.UpdatePlayerID(oldPID, c.ID)
|
||||
eng.SetBotControlled(c.ID, false)
|
||||
h.sendToChannel(c.Send, "game_start", map[string]interface{}{
|
||||
"your_player_id": c.ID, "harmony_target": eng.HarmonyTarget,
|
||||
"player_count": len(eng.Players), "rejoin": true,
|
||||
})
|
||||
snap := eng.Snapshot(c.ID)
|
||||
h.sendToChannel(c.Send, "state_snapshot", snap)
|
||||
}
|
||||
rm := h.RoomMgr.GetRoom(roomID)
|
||||
if rm != nil {
|
||||
h.broadcastRoomState(rm)
|
||||
}
|
||||
h.sendTo(c, "rejoin_result", map[string]interface{}{"success": true, "room_id": roomID})
|
||||
}
|
||||
|
||||
func (h *Hub) handleCreateRoom(c *Client, p map[string]interface{}) {
|
||||
nick, _ := p["nickname"].(string)
|
||||
mp := 4
|
||||
@@ -251,6 +356,7 @@ func (h *Hub) handleCreateRoom(c *Client, p map[string]interface{}) {
|
||||
rm := h.RoomMgr.CreateRoom(c.ID, nick, mp)
|
||||
c.RoomID = rm.ID
|
||||
rm.Players[c.ID].Send = c.Send
|
||||
rm.Players[c.ID].Username = c.Username
|
||||
h.sendTo(c, "room_created", map[string]interface{}{"room_id": rm.ID})
|
||||
h.broadcastRoomState(rm)
|
||||
}
|
||||
@@ -268,6 +374,8 @@ func (h *Hub) handleJoinRoom(c *Client, p map[string]interface{}) {
|
||||
}
|
||||
c.RoomID = rm.ID
|
||||
rm.Players[c.ID].Send = c.Send
|
||||
rm.Players[c.ID].Username = c.Username
|
||||
|
||||
h.sendTo(c, "room_joined", map[string]interface{}{"room_id": rm.ID})
|
||||
h.broadcastRoomState(rm)
|
||||
}
|
||||
@@ -298,6 +406,32 @@ func (h *Hub) handleSetChaos(c *Client, p map[string]interface{}) {
|
||||
h.broadcastRoomState(rm)
|
||||
}
|
||||
|
||||
func (h *Hub) handleCreateBotRoom(c *Client, p map[string]interface{}) {
|
||||
count := 4
|
||||
if v, ok := p["player_count"].(float64); ok {
|
||||
count = int(v)
|
||||
}
|
||||
if count < 3 || count > 6 {
|
||||
h.sendTo(c, "error", map[string]interface{}{"message": "人数必须在3-6之间"})
|
||||
return
|
||||
}
|
||||
rid := fmt.Sprintf("BOT_%s", generateClientID()[:6])
|
||||
h.RoomMgr.CreateTestRoom(rid, count)
|
||||
|
||||
rm, ok := h.RoomMgr.JoinRoom(rid, c.ID, c.Nickname)
|
||||
if !ok {
|
||||
h.sendTo(c, "error", map[string]interface{}{"message": "创建失败"})
|
||||
return
|
||||
}
|
||||
c.RoomID = rm.ID
|
||||
rm.Players[c.ID].Send = c.Send
|
||||
rm.Players[c.ID].Username = c.Username
|
||||
|
||||
h.sendTo(c, "room_joined", map[string]interface{}{"room_id": rm.ID})
|
||||
h.broadcastRoomState(rm)
|
||||
log.Printf("Bot room %s created by %s (%d players)", rid, c.Username, count)
|
||||
}
|
||||
|
||||
func (h *Hub) handleChat(c *Client, p map[string]interface{}) {
|
||||
if c.RoomID == "" {
|
||||
return
|
||||
@@ -528,7 +662,7 @@ func (h *Hub) processBotTurns(roomID string) {
|
||||
|
||||
for eng.Phase == "playing" && !eng.IsEffectResolving() {
|
||||
cp := eng.CurrentPlayer()
|
||||
if cp == nil || !cp.IsBot {
|
||||
if cp == nil || (!cp.IsBot && !cp.BotControlled) {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -556,6 +690,7 @@ func (h *Hub) processBotTurns(roomID string) {
|
||||
return
|
||||
}
|
||||
}
|
||||
h.startTurnTimer(roomID)
|
||||
}
|
||||
|
||||
func (h *Hub) handleAllExited(roomID string) {
|
||||
@@ -624,7 +759,65 @@ func (h *Hub) handleAllExited(roomID string) {
|
||||
h.broadcastRoomState(rm)
|
||||
}
|
||||
|
||||
// ---- Audio relay ----
|
||||
func (h *Hub) startTurnTimer(roomID string) {
|
||||
h.mu.RLock()
|
||||
eng := h.engines[roomID]
|
||||
h.mu.RUnlock()
|
||||
if eng == nil || eng.Phase != "playing" {
|
||||
return
|
||||
}
|
||||
cp := eng.CurrentPlayer()
|
||||
if cp == nil || cp.IsBot || cp.BotControlled {
|
||||
return
|
||||
}
|
||||
|
||||
turnID := eng.TurnID
|
||||
go func() {
|
||||
time.Sleep(30 * time.Second)
|
||||
h.mu.RLock()
|
||||
eng2 := h.engines[roomID]
|
||||
h.mu.RUnlock()
|
||||
if eng2 == nil || eng2.Phase != "playing" || eng2.TurnID != turnID {
|
||||
return
|
||||
}
|
||||
|
||||
cp2 := eng2.CurrentPlayer()
|
||||
if cp2 == nil {
|
||||
return
|
||||
}
|
||||
|
||||
eng2.ActionLogs = append(eng2.ActionLogs, cp2.Nickname+" 超时,自动出牌")
|
||||
action, target := eng2.BotAutoPlay(cp2.ID)
|
||||
if action == "" {
|
||||
return
|
||||
}
|
||||
|
||||
eng2.ConfirmAction(cp2.ID, action, target)
|
||||
h.broadcastSnapshots(roomID)
|
||||
h.flushGameLogs(roomID)
|
||||
if eng2.Phase == "all_exited" {
|
||||
h.handleAllExited(roomID)
|
||||
return
|
||||
}
|
||||
h.processBotTurns(roomID)
|
||||
}()
|
||||
}
|
||||
|
||||
// ---- Broadcast helpers ----
|
||||
|
||||
func (h *Hub) broadcastToRoom(roomID, msgType string, data map[string]interface{}) {
|
||||
rm := h.RoomMgr.GetRoom(roomID)
|
||||
if rm == nil {
|
||||
return
|
||||
}
|
||||
for _, pid := range rm.PlayerOrder {
|
||||
p := rm.Players[pid]
|
||||
if p.IsBot || p.Send == nil {
|
||||
continue
|
||||
}
|
||||
h.sendToChannel(p.Send, msgType, data)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) flushGameLogs(roomID string) {
|
||||
h.mu.RLock()
|
||||
|
||||
Reference in New Issue
Block a user