mirror of
https://github.com/tiennm99/gomoku.git
synced 2026-09-10 06:20:34 +00:00
feat: add documentation, code comments, and update Docker config
Rewrite README with usage guide, deployment instructions, and protocol docs. Update CLAUDE.md to reflect gomoku-only architecture. Add English doc comments to all key server Go files, replacing Chinese comments. Create docs/system-architecture.md (state machine, protocol, database schema) and docs/deployment-guide.md (local dev, Docker, production nginx, resource requirements). Update Dockerfile to Go 1.22 with repo-root build context to include web client. Update docker-compose to match.
This commit is contained in:
+14
-16
@@ -16,13 +16,14 @@ import (
|
||||
"github.com/ratel-online/server/consts"
|
||||
)
|
||||
|
||||
// In-memory data store. All state is volatile — server restart clears everything.
|
||||
var roomIds int64 = 0
|
||||
var players = hashmap.New() // 存储连接过服务器的全部用户
|
||||
var connPlayers = hashmap.New()
|
||||
var rooms = hashmap.New()
|
||||
var roomPlayers = hashmap.New()
|
||||
var roomSpectators = hashmap.New()
|
||||
var roomKickedPlayers = hashmap.New()
|
||||
var players = hashmap.New() // all players ever connected (by ID)
|
||||
var connPlayers = hashmap.New() // currently connected players
|
||||
var rooms = hashmap.New() // active rooms (by room ID)
|
||||
var roomPlayers = hashmap.New() // map[roomID] -> map[playerID]bool
|
||||
var roomSpectators = hashmap.New() // map[roomID] -> map[playerID]int (join order)
|
||||
var roomKickedPlayers = hashmap.New() // map[roomID] -> map[playerID]bool
|
||||
var roomPropsSetter = map[string]func(r *Room, v string){
|
||||
consts.RoomPropsPassword: func(r *Room, v string) {
|
||||
if v == "off" {
|
||||
@@ -59,9 +60,9 @@ func Connected(conn *network.Conn, info *modelx.AuthInfo) *Player {
|
||||
Name: strings.Desensitize(info.Name),
|
||||
Amount: 2000,
|
||||
}
|
||||
player.Conn(conn) // 初始化play对象
|
||||
players.Set(conn.ID(), player) // 写入用户池
|
||||
connPlayers.Set(conn.ID(), player) // 写入连接用户池
|
||||
player.Conn(conn)
|
||||
players.Set(conn.ID(), player)
|
||||
connPlayers.Set(conn.ID(), player)
|
||||
return player
|
||||
}
|
||||
|
||||
@@ -122,12 +123,11 @@ func getPlayer(playerId int64) *Player {
|
||||
}
|
||||
|
||||
func SetRoomProps(room *Room, k, v string) {
|
||||
// 根据房间类型限制可设置的属性
|
||||
// Only allow properties appropriate for the game type
|
||||
allowedProps := getAllowedPropsByGameType(room.Type)
|
||||
|
||||
// 检查属性是否允许设置
|
||||
if !allowedProps[k] {
|
||||
return // 不允许的属性直接返回,不执行设置
|
||||
return
|
||||
}
|
||||
|
||||
if setter, ok := roomPropsSetter[k]; ok {
|
||||
@@ -181,9 +181,9 @@ func IsValidPlayer(roomId, playerId int64) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 加入房间
|
||||
// JoinRoom adds a player to a room. If the room is full or running, the player
|
||||
// becomes a spectator instead. Returns an error if the player was previously kicked.
|
||||
func JoinRoom(roomId, playerId int64) error {
|
||||
// 资源检查
|
||||
player := getPlayer(playerId)
|
||||
if player == nil {
|
||||
return consts.ErrorsExist
|
||||
@@ -193,7 +193,6 @@ func JoinRoom(roomId, playerId int64) error {
|
||||
return consts.ErrorsRoomInvalid
|
||||
}
|
||||
|
||||
// 加锁防止并发异常
|
||||
room.Lock()
|
||||
defer room.Unlock()
|
||||
|
||||
@@ -203,7 +202,6 @@ func JoinRoom(roomId, playerId int64) error {
|
||||
|
||||
room.ActiveTime = time.Now()
|
||||
|
||||
//房间人数及状态检查
|
||||
if room.Players >= room.MaxPlayers || room.State == consts.RoomStateRunning {
|
||||
spectatorsIds := getRoomSpectators(roomId)
|
||||
spectatorsIds[playerId] = len(spectatorsIds)
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/ratel-online/server/consts"
|
||||
)
|
||||
|
||||
// Role represents a player's role within a room.
|
||||
type Role string
|
||||
|
||||
const (
|
||||
@@ -22,6 +23,12 @@ const (
|
||||
RoleSpectator Role = "spectator"
|
||||
)
|
||||
|
||||
// Player represents a connected user. Public fields are serializable state;
|
||||
// private fields manage the network connection and state machine lifecycle.
|
||||
//
|
||||
// The `data` channel receives packets from the client when `read` is true
|
||||
// (between StartTransaction/StopTransaction). The state machine goroutine
|
||||
// reads from this channel via AskFor* methods.
|
||||
type Player struct {
|
||||
ID int64 `json:"id"`
|
||||
IP string `json:"ip"`
|
||||
@@ -32,11 +39,11 @@ type Player struct {
|
||||
RoomID int64 `json:"roomId"`
|
||||
Role Role `json:"role"`
|
||||
|
||||
conn *network.Conn
|
||||
data chan *protocol.Packet
|
||||
read bool
|
||||
state consts.StateID
|
||||
online bool
|
||||
conn *network.Conn // underlying network connection
|
||||
data chan *protocol.Packet // buffered channel for client input
|
||||
read bool // true when accepting input (inside a transaction)
|
||||
state consts.StateID // current state machine state
|
||||
online bool // false after disconnect
|
||||
}
|
||||
|
||||
func (p *Player) Write(bytes []byte) error {
|
||||
@@ -49,6 +56,8 @@ func (p *Player) IsOnline() bool {
|
||||
return p.online
|
||||
}
|
||||
|
||||
// Offline marks the player as disconnected, closes the connection, and
|
||||
// cleans up room membership. Called when the network read loop exits.
|
||||
func (p *Player) Offline() {
|
||||
p.online = false
|
||||
_ = p.conn.Close()
|
||||
@@ -65,6 +74,9 @@ func (p *Player) Offline() {
|
||||
}
|
||||
}
|
||||
|
||||
// Listening is the main read loop. It reads packets from the network connection
|
||||
// and forwards them to the data channel when the state machine is accepting input.
|
||||
// Blocks until the connection is closed or errors.
|
||||
func (p *Player) Listening() error {
|
||||
loopCount := 0
|
||||
for {
|
||||
@@ -83,7 +95,8 @@ func (p *Player) Listening() error {
|
||||
}
|
||||
}
|
||||
|
||||
// 向客户端发生消息
|
||||
// WriteString sends a text message to the client. The 30ms sleep prevents
|
||||
// message flooding when the server sends multiple messages in rapid succession.
|
||||
func (p *Player) WriteString(data string) error {
|
||||
time.Sleep(30 * time.Millisecond)
|
||||
return p.conn.Write(protocol.Packet{
|
||||
@@ -165,11 +178,14 @@ func (p *Player) AskForStringWithoutTransaction(timeout ...time.Duration) (strin
|
||||
return packet.String(), nil
|
||||
}
|
||||
|
||||
// StartTransaction enables input acceptance and sends the INTERACTIVE_SIGNAL_START
|
||||
// marker to the client. The web client uses this to know when to enable the input field.
|
||||
func (p *Player) StartTransaction() {
|
||||
p.read = true
|
||||
_ = p.WriteString(consts.IsStart)
|
||||
}
|
||||
|
||||
// StopTransaction disables input acceptance and sends INTERACTIVE_SIGNAL_STOP.
|
||||
func (p *Player) StopTransaction() {
|
||||
p.read = false
|
||||
_ = p.WriteString(consts.IsStop)
|
||||
@@ -200,10 +216,14 @@ func (p Player) String() string {
|
||||
return fmt.Sprintf("%s[%d]", p.Name, p.ID)
|
||||
}
|
||||
|
||||
// RoomGame is implemented by each game type's state struct (e.g., Gomoku).
|
||||
// Clean() is called when the room is deleted to close channels and free resources.
|
||||
type RoomGame interface {
|
||||
Clean()
|
||||
}
|
||||
|
||||
// Room represents a game room. Players join a room, wait for enough players,
|
||||
// then the owner starts the game. The Game field holds the active game state.
|
||||
type Room struct {
|
||||
sync.Mutex
|
||||
|
||||
|
||||
Reference in New Issue
Block a user