168 lines
3.7 KiB
Go
168 lines
3.7 KiB
Go
package main
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"log"
|
||
|
|
"net/http"
|
||
|
|
"sync"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
"github.com/gorilla/websocket"
|
||
|
|
|
||
|
|
"mrcc/internal/config"
|
||
|
|
"mrcc/internal/httpserver"
|
||
|
|
"mrcc/internal/logger"
|
||
|
|
"mrcc/pkg/response"
|
||
|
|
)
|
||
|
|
|
||
|
|
// === 数据结构骨架(对照开发文档 18.5 节)===
|
||
|
|
|
||
|
|
type RoomStatus string
|
||
|
|
|
||
|
|
const (
|
||
|
|
RoomCreated RoomStatus = "created"
|
||
|
|
RoomWaiting RoomStatus = "waiting"
|
||
|
|
RoomPlaying RoomStatus = "playing"
|
||
|
|
RoomFinished RoomStatus = "finished"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Player 玩家连接
|
||
|
|
type Player struct {
|
||
|
|
UserID string `json:"user_id"`
|
||
|
|
Username string `json:"username"`
|
||
|
|
Conn *websocket.Conn `json:"-"`
|
||
|
|
IsReady bool `json:"is_ready"`
|
||
|
|
JoinedAt time.Time `json:"joined_at"`
|
||
|
|
SendQueue chan []byte `json:"-"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// Room 房间
|
||
|
|
type Room struct {
|
||
|
|
ID string `json:"id"`
|
||
|
|
Status RoomStatus `json:"status"`
|
||
|
|
HostUserID string `json:"host_user_id"`
|
||
|
|
MaxPlayers int `json:"max_players"`
|
||
|
|
Players map[string]*Player `json:"players"`
|
||
|
|
CreatedAt time.Time `json:"created_at"`
|
||
|
|
mu sync.RWMutex `json:"-"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// RelayServer 中转服务器
|
||
|
|
type RelayServer struct {
|
||
|
|
Rooms map[string]*Room
|
||
|
|
Players map[string]*Player
|
||
|
|
Register chan *Player
|
||
|
|
Unregister chan *Player
|
||
|
|
mu sync.RWMutex
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewRelayServer() *RelayServer {
|
||
|
|
return &RelayServer{
|
||
|
|
Rooms: make(map[string]*Room),
|
||
|
|
Players: make(map[string]*Player),
|
||
|
|
Register: make(chan *Player, 64),
|
||
|
|
Unregister: make(chan *Player, 64),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var upgrader = websocket.Upgrader{
|
||
|
|
CheckOrigin: func(r *http.Request) bool {
|
||
|
|
return true // 开发阶段允许所有来源
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
// HandleWS 处理 WebSocket 连接升级
|
||
|
|
func HandleWS(relay *RelayServer) gin.HandlerFunc {
|
||
|
|
return func(c *gin.Context) {
|
||
|
|
userID := c.Query("user_id")
|
||
|
|
if userID == "" {
|
||
|
|
userID = "anonymous"
|
||
|
|
}
|
||
|
|
|
||
|
|
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||
|
|
if err != nil {
|
||
|
|
log.Printf("websocket upgrade failed: %v", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
player := &Player{
|
||
|
|
UserID: userID,
|
||
|
|
Conn: conn,
|
||
|
|
JoinedAt: time.Now(),
|
||
|
|
SendQueue: make(chan []byte, 256),
|
||
|
|
}
|
||
|
|
|
||
|
|
relay.Register <- player
|
||
|
|
|
||
|
|
log.Printf("player connected: %s", userID)
|
||
|
|
|
||
|
|
// TODO: 实现读写 goroutine、房间管理、消息路由、断线重连
|
||
|
|
// 当前骨架仅保持连接并读取消息
|
||
|
|
go func() {
|
||
|
|
defer func() {
|
||
|
|
relay.Unregister <- player
|
||
|
|
conn.Close()
|
||
|
|
}()
|
||
|
|
|
||
|
|
for {
|
||
|
|
_, _, err := conn.ReadMessage()
|
||
|
|
if err != nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func main() {
|
||
|
|
cfg := config.Load("relay-server", "8086")
|
||
|
|
logger.WithService(cfg.Name).Info("starting relay server", "port", cfg.Port)
|
||
|
|
|
||
|
|
relay := NewRelayServer()
|
||
|
|
|
||
|
|
// 后台处理注册/注销
|
||
|
|
go func() {
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case player := <-relay.Register:
|
||
|
|
relay.mu.Lock()
|
||
|
|
relay.Players[player.UserID] = player
|
||
|
|
relay.mu.Unlock()
|
||
|
|
log.Printf("player registered: %s", player.UserID)
|
||
|
|
|
||
|
|
case player := <-relay.Unregister:
|
||
|
|
relay.mu.Lock()
|
||
|
|
delete(relay.Players, player.UserID)
|
||
|
|
relay.mu.Unlock()
|
||
|
|
log.Printf("player unregistered: %s", player.UserID)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
srv := httpserver.New(cfg)
|
||
|
|
|
||
|
|
// WebSocket 端点
|
||
|
|
srv.GET("/ws", HandleWS(relay))
|
||
|
|
|
||
|
|
// 房间信息端点(骨架)
|
||
|
|
srv.GET("/rooms", func(c *gin.Context) {
|
||
|
|
relay.mu.RLock()
|
||
|
|
defer relay.mu.RUnlock()
|
||
|
|
|
||
|
|
rooms := make([]gin.H, 0, len(relay.Rooms))
|
||
|
|
for id, room := range relay.Rooms {
|
||
|
|
rooms = append(rooms, gin.H{
|
||
|
|
"id": id,
|
||
|
|
"status": room.Status,
|
||
|
|
"playerCount": len(room.Players),
|
||
|
|
})
|
||
|
|
}
|
||
|
|
response.OK(c, gin.H{"rooms": rooms, "total": len(rooms)})
|
||
|
|
})
|
||
|
|
|
||
|
|
log.Printf("relay-server listening on %s (WS: /ws)", cfg.Address())
|
||
|
|
if err := srv.Run(cfg.Address()); err != nil {
|
||
|
|
log.Fatal(err)
|
||
|
|
}
|
||
|
|
}
|