mirror of
https://github.com/tiennm99/gomoku.git
synced 2026-05-14 04:58:38 +00:00
refactor(lobby): drop 'New' prefix from Room type and store functions
The 'New' prefix was a phase-06 naming compromise to avoid colliding with a legacy Room type. That legacy type is gone now, so the prefix is misleading — in Go 'NewX' conventionally reads as a constructor, not a type. - NewRoom type → Room - JoinNewRoom → JoinRoom - GetNewRoom → GetRoom - LeaveNewRoom → LeaveRoom - WatchNewRoom → WatchRoom - UnwatchNewRoom → UnwatchRoom - deleteNewRoom → deleteRoom Also remove dead code uncovered by the pass: - Delete lobby/events.go entirely — RoomEvent + BroadcastEvent wrapped a no-op placeholder, never called. - Delete BroadcastToNewRoom no-op from lobby/store.go — kept only for the dead BroadcastEvent wrapper. - Delete TestBroadcastToRoom_SkipsExcludedIDs — theater test that never actually called BroadcastToNewRoom, just built a manual exclude map. go vet + go test ./... green.
This commit is contained in:
@@ -38,7 +38,7 @@ func reapIdleRooms() {
|
||||
if nPlayers == 0 && nSpectators == 0 && time.Since(lastActive) > idleRoomTimeout {
|
||||
log.Infof("[cleanup] removing idle room %d (no activity for >%s)\n", id, idleRoomTimeout)
|
||||
store.mu.Lock()
|
||||
deleteNewRoom(id)
|
||||
deleteRoom(id)
|
||||
store.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package lobby
|
||||
|
||||
// RoomEvent is a message broadcast to all participants in a room (players + spectators).
|
||||
// Simplest form: a plain string payload. Phase-07 will expand this with typed events
|
||||
// for the spectator fan-out goroutine.
|
||||
type RoomEvent struct {
|
||||
Payload string
|
||||
}
|
||||
|
||||
// BroadcastEvent sends a RoomEvent to every player and spectator in the room,
|
||||
// excluding the IDs listed in excludePlayerIDs.
|
||||
// Acquires room.RLock internally; must NOT be called while holding room.Lock().
|
||||
func BroadcastEvent(room *NewRoom, event RoomEvent, excludePlayerIDs ...int64) {
|
||||
BroadcastToNewRoom(room, event.Payload, excludePlayerIDs...)
|
||||
}
|
||||
@@ -33,14 +33,14 @@ type GameMove struct {
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
// NewRoom is the new domain model for a game room.
|
||||
// Room is the new domain model for a game room.
|
||||
// It embeds game.Board directly and carries all fields from the caro Java domain:
|
||||
// blackPlayerId, whitePlayerId, currentTurn, moveHistory, watcherList, roomOwner,
|
||||
// type (PVP|PVE), difficultyCoefficient.
|
||||
//
|
||||
// sync.RWMutex protects Board, Players, Spectators, MoveHistory, and Status.
|
||||
// Never hold the lock while calling player.WriteString/sendFn — copy fields first, then release.
|
||||
type NewRoom struct {
|
||||
type Room struct {
|
||||
sync.RWMutex
|
||||
|
||||
ID int64
|
||||
@@ -86,7 +86,7 @@ type NewRoom struct {
|
||||
|
||||
// ApplyMove places a piece at (row, col), appends to MoveHistory, flips CurrentTurn,
|
||||
// and returns the new GameResult. Caller must hold room.Lock().
|
||||
func (r *NewRoom) ApplyMove(row, col int, piece game.Piece, playerID int64) (game.GameResult, error) {
|
||||
func (r *Room) ApplyMove(row, col int, piece game.Piece, playerID int64) (game.GameResult, error) {
|
||||
if !r.Board.MakeMove(row, col, piece) {
|
||||
return game.InProgress, ErrRoomFull // reuse as "invalid move" sentinel; phase-06 adds ErrInvalidMove
|
||||
}
|
||||
@@ -108,7 +108,7 @@ func (r *NewRoom) ApplyMove(row, col int, piece game.Piece, playerID int64) (gam
|
||||
|
||||
// Reset clears the board and history, returning the room to WAITING status.
|
||||
// For PVE rooms it re-randomizes color assignment and creates a fresh AI.
|
||||
func (r *NewRoom) Reset(rng int64) {
|
||||
func (r *Room) Reset(rng int64) {
|
||||
r.Board.Reset()
|
||||
r.MoveHistory = nil
|
||||
r.CurrentTurn = game.Black
|
||||
@@ -141,7 +141,7 @@ type RoomSnapshot struct {
|
||||
|
||||
// Snapshot returns a consistent read of room state safe to send without holding the lock.
|
||||
// Caller must NOT hold any lock when calling this — it acquires RLock internally.
|
||||
func (r *NewRoom) Snapshot() RoomSnapshot {
|
||||
func (r *Room) Snapshot() RoomSnapshot {
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
|
||||
@@ -170,11 +170,11 @@ func (r *NewRoom) Snapshot() RoomSnapshot {
|
||||
// PlayerCount returns the number of human players currently in the room.
|
||||
// Caller may or may not hold RLock — reads len which is safe under Go's memory model
|
||||
// only when protected; callers should hold at least RLock for correctness.
|
||||
func (r *NewRoom) PlayerCount() int {
|
||||
func (r *Room) PlayerCount() int {
|
||||
return len(r.Players)
|
||||
}
|
||||
|
||||
// IsOwner returns true if playerID is the room owner.
|
||||
func (r *NewRoom) IsOwner(playerID int64) bool {
|
||||
func (r *Room) IsOwner(playerID int64) bool {
|
||||
return r.OwnerID == playerID
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"github.com/tiennm99/gomoku/server/game"
|
||||
)
|
||||
|
||||
// newTestRoom builds a minimal NewRoom for unit tests without going through the store.
|
||||
func newTestRoom(t RoomType, ownerID int64) *NewRoom {
|
||||
r := &NewRoom{
|
||||
// newTestRoom builds a minimal Room for unit tests without going through the store.
|
||||
func newTestRoom(t RoomType, ownerID int64) *Room {
|
||||
r := &Room{
|
||||
ID: 1,
|
||||
OwnerID: ownerID,
|
||||
OwnerNickname: "owner",
|
||||
|
||||
+24
-31
@@ -19,7 +19,7 @@ import (
|
||||
// - Per-room mutations use room.Lock() / room.RLock() independently.
|
||||
var store = &roomStore{
|
||||
players: make(map[int64]*Player),
|
||||
rooms: make(map[int64]*NewRoom),
|
||||
rooms: make(map[int64]*Room),
|
||||
nextPlayerID: 1,
|
||||
nextRoomID: 1,
|
||||
}
|
||||
@@ -27,7 +27,7 @@ var store = &roomStore{
|
||||
type roomStore struct {
|
||||
mu sync.RWMutex
|
||||
players map[int64]*Player
|
||||
rooms map[int64]*NewRoom
|
||||
rooms map[int64]*Room
|
||||
nextPlayerID int64
|
||||
nextRoomID int64
|
||||
}
|
||||
@@ -65,18 +65,18 @@ func RemovePlayer(id int64) {
|
||||
}
|
||||
store.mu.Unlock()
|
||||
if ok && p.RoomID != 0 {
|
||||
LeaveNewRoom(p)
|
||||
LeaveRoom(p)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Room creation ---
|
||||
|
||||
// CreatePvpRoom creates a PVP room owned by the given player.
|
||||
func CreatePvpRoom(owner *Player) (*NewRoom, error) {
|
||||
func CreatePvpRoom(owner *Player) (*Room, error) {
|
||||
store.mu.Lock()
|
||||
id := store.nextRoomID
|
||||
store.nextRoomID++
|
||||
r := &NewRoom{
|
||||
r := &Room{
|
||||
ID: id,
|
||||
OwnerID: owner.ID,
|
||||
OwnerNickname: owner.Name,
|
||||
@@ -101,7 +101,7 @@ func CreatePvpRoom(owner *Player) (*NewRoom, error) {
|
||||
|
||||
// CreatePveRoom creates a PVE room. difficulty must be 1, 2, or 3.
|
||||
// The human player is randomly assigned Black or White; the AI takes the other side.
|
||||
func CreatePveRoom(owner *Player, difficulty int) (*NewRoom, error) {
|
||||
func CreatePveRoom(owner *Player, difficulty int) (*Room, error) {
|
||||
if difficulty < 1 || difficulty > 3 {
|
||||
return nil, ErrInvalidDifficulty
|
||||
}
|
||||
@@ -133,7 +133,7 @@ func CreatePveRoom(owner *Player, difficulty int) (*NewRoom, error) {
|
||||
store.mu.Lock()
|
||||
id := store.nextRoomID
|
||||
store.nextRoomID++
|
||||
r := &NewRoom{
|
||||
r := &Room{
|
||||
ID: id,
|
||||
OwnerID: owner.ID,
|
||||
OwnerNickname: owner.Name,
|
||||
@@ -158,8 +158,8 @@ func CreatePveRoom(owner *Player, difficulty int) (*NewRoom, error) {
|
||||
|
||||
// --- Room lookups ---
|
||||
|
||||
// GetNewRoom returns the room with the given ID, or (nil, false) if not found.
|
||||
func GetNewRoom(id int64) (*NewRoom, bool) {
|
||||
// GetRoom returns the room with the given ID, or (nil, false) if not found.
|
||||
func GetRoom(id int64) (*Room, bool) {
|
||||
store.mu.RLock()
|
||||
r, ok := store.rooms[id]
|
||||
store.mu.RUnlock()
|
||||
@@ -168,9 +168,9 @@ func GetNewRoom(id int64) (*NewRoom, bool) {
|
||||
|
||||
// GetAllRooms returns a snapshot slice of all rooms sorted by ID ascending.
|
||||
// The slice is a copy; callers must not mutate rooms through it without locking.
|
||||
func GetAllRooms() []*NewRoom {
|
||||
func GetAllRooms() []*Room {
|
||||
store.mu.RLock()
|
||||
list := make([]*NewRoom, 0, len(store.rooms))
|
||||
list := make([]*Room, 0, len(store.rooms))
|
||||
for _, r := range store.rooms {
|
||||
list = append(list, r)
|
||||
}
|
||||
@@ -179,17 +179,17 @@ func GetAllRooms() []*NewRoom {
|
||||
return list
|
||||
}
|
||||
|
||||
// deleteNewRoom removes a room from the store. Caller must hold store.mu.Lock().
|
||||
func deleteNewRoom(id int64) {
|
||||
// deleteRoom removes a room from the store. Caller must hold store.mu.Lock().
|
||||
func deleteRoom(id int64) {
|
||||
delete(store.rooms, id)
|
||||
}
|
||||
|
||||
// --- Room membership ---
|
||||
|
||||
// JoinNewRoom adds a player to a room as a human participant.
|
||||
// JoinRoom adds a player to a room as a human participant.
|
||||
// Returns ErrRoomNotFound, ErrRoomFull, or ErrRoomPlaying on failure.
|
||||
// On success, sets player.RoomID.
|
||||
func JoinNewRoom(roomID int64, player *Player) error {
|
||||
func JoinRoom(roomID int64, player *Player) error {
|
||||
store.mu.RLock()
|
||||
r, ok := store.rooms[roomID]
|
||||
store.mu.RUnlock()
|
||||
@@ -221,10 +221,10 @@ func JoinNewRoom(roomID int64, player *Player) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LeaveNewRoom removes a player from their current room (Players or Spectators).
|
||||
// LeaveRoom removes a player from their current room (Players or Spectators).
|
||||
// If the room becomes empty it is deleted from the store.
|
||||
// Broadcasts nothing — callers handle messaging.
|
||||
func LeaveNewRoom(player *Player) {
|
||||
func LeaveRoom(player *Player) {
|
||||
if player.RoomID == 0 {
|
||||
return
|
||||
}
|
||||
@@ -269,7 +269,7 @@ func LeaveNewRoom(player *Player) {
|
||||
for _, sp := range r.Spectators {
|
||||
spectatorsToEject = append(spectatorsToEject, sp)
|
||||
}
|
||||
// Clear spectator map so UnwatchNewRoom calls from their state machines are no-ops.
|
||||
// Clear spectator map so UnwatchRoom calls from their state machines are no-ops.
|
||||
r.Spectators = make(map[int64]*Player)
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ func LeaveNewRoom(player *Player) {
|
||||
|
||||
if empty {
|
||||
store.mu.Lock()
|
||||
deleteNewRoom(roomID)
|
||||
deleteRoom(roomID)
|
||||
store.mu.Unlock()
|
||||
}
|
||||
}
|
||||
@@ -334,9 +334,9 @@ func pushWatchGameExitToCmdCh(player *Player) {
|
||||
}
|
||||
}
|
||||
|
||||
// WatchNewRoom adds a player to a room's Spectators list.
|
||||
// WatchRoom adds a player to a room's Spectators list.
|
||||
// Returns ErrRoomNotFound if the room does not exist.
|
||||
func WatchNewRoom(roomID int64, player *Player) error {
|
||||
func WatchRoom(roomID int64, player *Player) error {
|
||||
store.mu.RLock()
|
||||
r, ok := store.rooms[roomID]
|
||||
store.mu.RUnlock()
|
||||
@@ -354,8 +354,8 @@ func WatchNewRoom(roomID int64, player *Player) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnwatchNewRoom removes a spectator from their room.
|
||||
func UnwatchNewRoom(player *Player) {
|
||||
// UnwatchRoom removes a spectator from their room.
|
||||
func UnwatchRoom(player *Player) {
|
||||
if player.RoomID == 0 {
|
||||
return
|
||||
}
|
||||
@@ -379,14 +379,7 @@ func UnwatchNewRoom(player *Player) {
|
||||
|
||||
if empty {
|
||||
store.mu.Lock()
|
||||
deleteNewRoom(roomID)
|
||||
deleteRoom(roomID)
|
||||
store.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// BroadcastToNewRoom is a placeholder kept for BroadcastEvent compatibility.
|
||||
// The new state machine uses state.broadcastResponse (typed protobuf) instead.
|
||||
// String-based broadcast is no longer supported after phase-06.
|
||||
func BroadcastToNewRoom(_ *NewRoom, _ string, _ ...int64) {
|
||||
// no-op: all broadcasts now use typed *protocol.Response via player.Send
|
||||
}
|
||||
|
||||
+17
-76
@@ -2,7 +2,6 @@ package lobby
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -10,7 +9,7 @@ import (
|
||||
func resetStore() {
|
||||
store.mu.Lock()
|
||||
store.players = make(map[int64]*Player)
|
||||
store.rooms = make(map[int64]*NewRoom)
|
||||
store.rooms = make(map[int64]*Room)
|
||||
store.nextPlayerID = 1
|
||||
store.nextRoomID = 1
|
||||
store.mu.Unlock()
|
||||
@@ -104,7 +103,7 @@ func TestCreatePveRoom_InvalidDifficultyRejected(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- JoinNewRoom tests ---
|
||||
// --- JoinRoom tests ---
|
||||
|
||||
func TestJoinRoom_FullRejected(t *testing.T) {
|
||||
resetStore()
|
||||
@@ -113,13 +112,13 @@ func TestJoinRoom_FullRejected(t *testing.T) {
|
||||
p3 := makePlayer("frank")
|
||||
|
||||
r, _ := CreatePvpRoom(owner)
|
||||
if err := JoinNewRoom(r.ID, owner); err != nil {
|
||||
if err := JoinRoom(r.ID, owner); err != nil {
|
||||
t.Fatalf("owner join: %v", err)
|
||||
}
|
||||
if err := JoinNewRoom(r.ID, p2); err != nil {
|
||||
if err := JoinRoom(r.ID, p2); err != nil {
|
||||
t.Fatalf("second player join: %v", err)
|
||||
}
|
||||
if err := JoinNewRoom(r.ID, p3); err != ErrRoomFull {
|
||||
if err := JoinRoom(r.ID, p3); err != ErrRoomFull {
|
||||
t.Errorf("expected ErrRoomFull, got %v", err)
|
||||
}
|
||||
}
|
||||
@@ -128,32 +127,32 @@ func TestJoinRoom_NotFoundRejected(t *testing.T) {
|
||||
resetStore()
|
||||
p := makePlayer("ghost")
|
||||
|
||||
err := JoinNewRoom(999, p)
|
||||
err := JoinRoom(999, p)
|
||||
if err != ErrRoomNotFound {
|
||||
t.Errorf("expected ErrRoomNotFound, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// --- LeaveNewRoom tests ---
|
||||
// --- LeaveRoom tests ---
|
||||
|
||||
func TestLeaveRoom_RemovesPlayerAndCleansEmptyRoom(t *testing.T) {
|
||||
resetStore()
|
||||
owner := makePlayer("grace")
|
||||
|
||||
r, _ := CreatePvpRoom(owner)
|
||||
_ = JoinNewRoom(r.ID, owner)
|
||||
_ = JoinRoom(r.ID, owner)
|
||||
|
||||
LeaveNewRoom(owner)
|
||||
LeaveRoom(owner)
|
||||
|
||||
if owner.RoomID != 0 {
|
||||
t.Errorf("player.RoomID should be 0 after leave, got %d", owner.RoomID)
|
||||
}
|
||||
if _, ok := GetNewRoom(r.ID); ok {
|
||||
if _, ok := GetRoom(r.ID); ok {
|
||||
t.Error("empty room should be deleted from store")
|
||||
}
|
||||
}
|
||||
|
||||
// --- WatchNewRoom / UnwatchNewRoom tests ---
|
||||
// --- WatchRoom / UnwatchRoom tests ---
|
||||
|
||||
func TestWatchRoom_AddsSpectator(t *testing.T) {
|
||||
resetStore()
|
||||
@@ -161,10 +160,10 @@ func TestWatchRoom_AddsSpectator(t *testing.T) {
|
||||
spectator := makePlayer("iris")
|
||||
|
||||
r, _ := CreatePvpRoom(owner)
|
||||
_ = JoinNewRoom(r.ID, owner)
|
||||
_ = JoinRoom(r.ID, owner)
|
||||
|
||||
if err := WatchNewRoom(r.ID, spectator); err != nil {
|
||||
t.Fatalf("WatchNewRoom: %v", err)
|
||||
if err := WatchRoom(r.ID, spectator); err != nil {
|
||||
t.Fatalf("WatchRoom: %v", err)
|
||||
}
|
||||
|
||||
r.RLock()
|
||||
@@ -185,10 +184,10 @@ func TestUnwatchRoom_RemovesSpectator(t *testing.T) {
|
||||
spectator := makePlayer("kim")
|
||||
|
||||
r, _ := CreatePvpRoom(owner)
|
||||
_ = JoinNewRoom(r.ID, owner)
|
||||
_ = WatchNewRoom(r.ID, spectator)
|
||||
_ = JoinRoom(r.ID, owner)
|
||||
_ = WatchRoom(r.ID, spectator)
|
||||
|
||||
UnwatchNewRoom(spectator)
|
||||
UnwatchRoom(spectator)
|
||||
|
||||
r.RLock()
|
||||
_, found := r.Spectators[spectator.ID]
|
||||
@@ -227,61 +226,3 @@ func TestGetAllRooms_ReturnsSnapshot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Broadcast test ---
|
||||
|
||||
func TestBroadcastToRoom_SkipsExcludedIDs(t *testing.T) {
|
||||
resetStore()
|
||||
|
||||
type call struct{ id int64 }
|
||||
var mu sync.Mutex
|
||||
var calls []call
|
||||
|
||||
makeFakePlayer := func(name string) *Player {
|
||||
p := RegisterPlayer(name)
|
||||
// Override WriteString via a wrapper conn — simplest: capture via closure
|
||||
// by replacing the data channel and using a custom approach.
|
||||
// Since Player.WriteString uses p.conn which is nil in tests,
|
||||
// we verify via BroadcastToNewRoom's internal path.
|
||||
// We'll wire sendFn indirectly: use a testable broadcast helper.
|
||||
_ = p // suppress unused warning
|
||||
return p
|
||||
}
|
||||
|
||||
// Use a direct test of BroadcastToNewRoom's exclude logic via targets list.
|
||||
// We build a room, manually wire WriteString-compatible players,
|
||||
// then assert excluded player never receives the message.
|
||||
|
||||
owner := makeFakePlayer("maya")
|
||||
other := makeFakePlayer("ned")
|
||||
|
||||
r, _ := CreatePvpRoom(owner)
|
||||
r.Lock()
|
||||
r.Players[owner.ID] = owner
|
||||
r.Players[other.ID] = other
|
||||
r.Unlock()
|
||||
|
||||
// Replace WriteString with a recorder via a thin intercept.
|
||||
// Since Player.WriteString calls p.conn.Write and conn is nil in tests,
|
||||
// we test the exclude logic directly: collect targets without nil-conn panic.
|
||||
r.RLock()
|
||||
var targets []int64
|
||||
for id := range r.Players {
|
||||
targets = append(targets, id)
|
||||
}
|
||||
r.RUnlock()
|
||||
|
||||
excluded := map[int64]bool{owner.ID: true}
|
||||
var reached []int64
|
||||
mu.Lock()
|
||||
for _, id := range targets {
|
||||
if !excluded[id] {
|
||||
reached = append(reached, id)
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
if len(reached) != 1 || reached[0] != other.ID {
|
||||
t.Errorf("expected only other.ID=%d in reached, got %v", other.ID, reached)
|
||||
}
|
||||
_ = calls // suppress unused
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
type gamePveState struct{}
|
||||
|
||||
func (*gamePveState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
room, ok := lobby.GetNewRoom(player.RoomID)
|
||||
room, ok := lobby.GetRoom(player.RoomID)
|
||||
if !ok {
|
||||
log.Errorf("[pve] player %d: room not found\n", player.ID)
|
||||
return consts.StateHome, nil
|
||||
@@ -64,7 +64,7 @@ func (*gamePveState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
|
||||
// applyHumanMove validates/applies the human's move then triggers AI response.
|
||||
// Returns (nextState, true) when the state should change; (0, false) on invalid move.
|
||||
func applyHumanMove(player *lobby.Player, room *lobby.NewRoom, humanPiece game.Piece, row, col int) (consts.StateID, bool) {
|
||||
func applyHumanMove(player *lobby.Player, room *lobby.Room, humanPiece game.Piece, row, col int) (consts.StateID, bool) {
|
||||
if row < 0 || row >= game.BoardSize || col < 0 || col >= game.BoardSize {
|
||||
_ = player.Send(&protocol.Response{
|
||||
Payload: &protocol.Response_GameMoveOutOfBounds{
|
||||
@@ -105,7 +105,7 @@ func applyHumanMove(player *lobby.Player, room *lobby.NewRoom, humanPiece game.P
|
||||
|
||||
// runAIMove computes and applies the AI's next move, broadcasts it, and checks result.
|
||||
// Returns (nextState, true) if the game ends, (0, false) to continue.
|
||||
func runAIMove(room *lobby.NewRoom) (consts.StateID, bool) {
|
||||
func runAIMove(room *lobby.Room) (consts.StateID, bool) {
|
||||
room.RLock()
|
||||
ai := room.AI
|
||||
board := room.Board.Clone() // safe value copy for AI computation
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
// setupPveGame creates a PVE room with the human assigned Black (AI=White).
|
||||
// seed=42 gives deterministic AI behavior.
|
||||
func setupPveGame(t *testing.T, difficulty int) (human *lobby.Player, room *lobby.NewRoom) {
|
||||
func setupPveGame(t *testing.T, difficulty int) (human *lobby.Player, room *lobby.Room) {
|
||||
t.Helper()
|
||||
human = makeRegisteredPlayer(t, "Human")
|
||||
|
||||
@@ -20,8 +20,8 @@ func setupPveGame(t *testing.T, difficulty int) (human *lobby.Player, room *lobb
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePveRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, human); err != nil {
|
||||
t.Fatalf("JoinNewRoom human: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, human); err != nil {
|
||||
t.Fatalf("JoinRoom human: %v", err)
|
||||
}
|
||||
|
||||
// Override AI with fixed seed for determinism.
|
||||
@@ -180,8 +180,8 @@ func TestPveAIFirstWhenHumanIsWhite(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePveRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, human); err != nil {
|
||||
t.Fatalf("JoinNewRoom: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, human); err != nil {
|
||||
t.Fatalf("JoinRoom: %v", err)
|
||||
}
|
||||
|
||||
// Force human = White, AI = Black.
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
type gamePvpState struct{}
|
||||
|
||||
func (*gamePvpState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
room, ok := lobby.GetNewRoom(player.RoomID)
|
||||
room, ok := lobby.GetRoom(player.RoomID)
|
||||
if !ok {
|
||||
log.Errorf("[pvp] player %d: room not found\n", player.ID)
|
||||
return consts.StateHome, nil
|
||||
@@ -74,7 +74,7 @@ func (*gamePvpState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
|
||||
// signalGameOver closes room.GameOverCh exactly once so the other player goroutine
|
||||
// unblocks and transitions to StateGameOver.
|
||||
func signalGameOver(room *lobby.NewRoom) {
|
||||
func signalGameOver(room *lobby.Room) {
|
||||
room.Lock()
|
||||
ch := room.GameOverCh
|
||||
if ch != nil {
|
||||
@@ -88,7 +88,7 @@ func signalGameOver(room *lobby.NewRoom) {
|
||||
|
||||
// applyPvpMove validates and applies a move. Returns (nextState, true) when
|
||||
// the state should change; (0, false) when move is rejected (stay in loop).
|
||||
func applyPvpMove(player *lobby.Player, room *lobby.NewRoom, row, col int) (consts.StateID, bool) {
|
||||
func applyPvpMove(player *lobby.Player, room *lobby.Room, row, col int) (consts.StateID, bool) {
|
||||
myPiece := playerPieceInRoom(room, player.ID)
|
||||
if myPiece == game.Empty {
|
||||
log.Errorf("[pvp] player %d not assigned a piece in room %d\n", player.ID, room.ID)
|
||||
@@ -151,7 +151,7 @@ func applyPvpMove(player *lobby.Player, room *lobby.NewRoom, row, col int) (cons
|
||||
}
|
||||
|
||||
// broadcastForfeit sends a GameOver response declaring the opponent as winner.
|
||||
func broadcastForfeit(room *lobby.NewRoom, disconnected *lobby.Player) {
|
||||
func broadcastForfeit(room *lobby.Room, disconnected *lobby.Player) {
|
||||
room.RLock()
|
||||
var winner *lobby.Player
|
||||
for id, p := range room.Players {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// setupPvpGame creates a fully-started PVP room (2 players, colors assigned, status=Playing).
|
||||
func setupPvpGame(t *testing.T) (black *lobby.Player, white *lobby.Player, room *lobby.NewRoom) {
|
||||
func setupPvpGame(t *testing.T) (black *lobby.Player, white *lobby.Player, room *lobby.Room) {
|
||||
t.Helper()
|
||||
black = makeRegisteredPlayer(t, "Black")
|
||||
white = makeRegisteredPlayer(t, "White")
|
||||
@@ -20,11 +20,11 @@ func setupPvpGame(t *testing.T) (black *lobby.Player, white *lobby.Player, room
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePvpRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, black); err != nil {
|
||||
t.Fatalf("JoinNewRoom black: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, black); err != nil {
|
||||
t.Fatalf("JoinRoom black: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, white); err != nil {
|
||||
t.Fatalf("JoinNewRoom white: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, white); err != nil {
|
||||
t.Fatalf("JoinRoom white: %v", err)
|
||||
}
|
||||
|
||||
// Assign colors deterministically.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
// sendRoomSnapshot sends the current room state to a newly-joined spectator.
|
||||
// Order: WatchGameSuccessResponse → GameStartingResponse → one GameMoveSuccessResponse per history entry.
|
||||
// Uses Snapshot() to avoid holding a lock while calling player.Send.
|
||||
func sendRoomSnapshot(player *lobby.Player, room *lobby.NewRoom) {
|
||||
func sendRoomSnapshot(player *lobby.Player, room *lobby.Room) {
|
||||
snap := room.Snapshot()
|
||||
|
||||
status := protocol.RoomStatus_WAITING
|
||||
@@ -62,7 +62,7 @@ func sendRoomSnapshot(player *lobby.Player, room *lobby.NewRoom) {
|
||||
|
||||
// buildGameStartingResponse constructs a GameStartingResponse from room state.
|
||||
// Acquires RLock internally for safety.
|
||||
func buildGameStartingResponse(room *lobby.NewRoom) *protocol.Response {
|
||||
func buildGameStartingResponse(room *lobby.Room) *protocol.Response {
|
||||
room.RLock()
|
||||
blackID := room.BlackPlayerID
|
||||
whiteID := room.WhitePlayerID
|
||||
@@ -87,7 +87,7 @@ func buildGameStartingResponse(room *lobby.NewRoom) *protocol.Response {
|
||||
}
|
||||
|
||||
// resolveNickname maps a playerID to a nickname. Returns "AI" for -1 (PVE AI slot).
|
||||
func resolveNickname(room *lobby.NewRoom, playerID int64) string {
|
||||
func resolveNickname(room *lobby.Room, playerID int64) string {
|
||||
if playerID == -1 {
|
||||
return "AI"
|
||||
}
|
||||
@@ -141,7 +141,7 @@ func buildGameOverResponse(result game.GameResult, winnerNickname string) *proto
|
||||
|
||||
// broadcastResponse sends resp to all players and spectators in the room.
|
||||
// Acquires RLock internally; must NOT be called while holding room.Lock().
|
||||
func broadcastResponse(room *lobby.NewRoom, resp *protocol.Response) {
|
||||
func broadcastResponse(room *lobby.Room, resp *protocol.Response) {
|
||||
room.RLock()
|
||||
targets := make([]*lobby.Player, 0, len(room.Players)+len(room.Spectators))
|
||||
for _, p := range room.Players {
|
||||
@@ -159,7 +159,7 @@ func broadcastResponse(room *lobby.NewRoom, resp *protocol.Response) {
|
||||
|
||||
// playerPieceInRoom returns the game.Piece assigned to playerID in room.
|
||||
// Returns game.Empty if not assigned.
|
||||
func playerPieceInRoom(room *lobby.NewRoom, playerID int64) game.Piece {
|
||||
func playerPieceInRoom(room *lobby.Room, playerID int64) game.Piece {
|
||||
room.RLock()
|
||||
black := room.BlackPlayerID
|
||||
white := room.WhitePlayerID
|
||||
@@ -175,7 +175,7 @@ func playerPieceInRoom(room *lobby.NewRoom, playerID int64) game.Piece {
|
||||
|
||||
// winnerNicknameFor returns the display name of the winner given a GameResult.
|
||||
// Returns empty string for a draw.
|
||||
func winnerNicknameFor(room *lobby.NewRoom, result game.GameResult) string {
|
||||
func winnerNicknameFor(room *lobby.Room, result game.GameResult) string {
|
||||
room.RLock()
|
||||
blackID := room.BlackPlayerID
|
||||
whiteID := room.WhitePlayerID
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
type gameOverState struct{}
|
||||
|
||||
func (*gameOverState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
room, ok := lobby.GetNewRoom(player.RoomID)
|
||||
room, ok := lobby.GetRoom(player.RoomID)
|
||||
if !ok {
|
||||
// Room gone (e.g. opponent left and room was deleted) — go home.
|
||||
return consts.StateHome, nil
|
||||
@@ -45,7 +45,7 @@ func (*gameOverState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
}
|
||||
|
||||
// handleGameReset resets the room and starts a fresh game.
|
||||
func handleGameReset(room *lobby.NewRoom) (consts.StateID, error) {
|
||||
func handleGameReset(room *lobby.Room) (consts.StateID, error) {
|
||||
seed := rand.Int63()
|
||||
room.Lock()
|
||||
room.Reset(seed)
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// setupFinishedPvpRoom creates a PVP room in Finished state for gameover tests.
|
||||
func setupFinishedPvpRoom(t *testing.T) (*lobby.Player, *lobby.Player, *lobby.NewRoom) {
|
||||
func setupFinishedPvpRoom(t *testing.T) (*lobby.Player, *lobby.Player, *lobby.Room) {
|
||||
t.Helper()
|
||||
black := makeRegisteredPlayer(t, "Black")
|
||||
white := makeRegisteredPlayer(t, "White")
|
||||
@@ -20,11 +20,11 @@ func setupFinishedPvpRoom(t *testing.T) (*lobby.Player, *lobby.Player, *lobby.Ne
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePvpRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, black); err != nil {
|
||||
t.Fatalf("JoinNewRoom black: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, black); err != nil {
|
||||
t.Fatalf("JoinRoom black: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, white); err != nil {
|
||||
t.Fatalf("JoinNewRoom white: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, white); err != nil {
|
||||
t.Fatalf("JoinRoom white: %v", err)
|
||||
}
|
||||
|
||||
room.Lock()
|
||||
@@ -82,8 +82,8 @@ func TestGameoverResetTransitionsToPve(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePveRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, human); err != nil {
|
||||
t.Fatalf("JoinNewRoom: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, human); err != nil {
|
||||
t.Fatalf("JoinRoom: %v", err)
|
||||
}
|
||||
|
||||
room.Lock()
|
||||
@@ -154,8 +154,8 @@ func TestGameoverPveResetRandomizesColors(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePveRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, human); err != nil {
|
||||
t.Fatalf("JoinNewRoom: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, human); err != nil {
|
||||
t.Fatalf("JoinRoom: %v", err)
|
||||
}
|
||||
|
||||
room.Lock()
|
||||
|
||||
@@ -49,8 +49,8 @@ func handleCreateRoom(player *lobby.Player) (consts.StateID, error) {
|
||||
}
|
||||
|
||||
// Owner joins as a player immediately.
|
||||
if err := lobby.JoinNewRoom(room.ID, player); err != nil {
|
||||
log.Errorf("[home] JoinNewRoom error player %d room %d: %v\n", player.ID, room.ID, err)
|
||||
if err := lobby.JoinRoom(room.ID, player); err != nil {
|
||||
log.Errorf("[home] JoinRoom error player %d room %d: %v\n", player.ID, room.ID, err)
|
||||
return consts.StateHome, nil
|
||||
}
|
||||
|
||||
@@ -90,8 +90,8 @@ func handleCreatePveRoom(player *lobby.Player, req *protocol.Request) (consts.St
|
||||
return consts.StateHome, nil
|
||||
}
|
||||
|
||||
if err := lobby.JoinNewRoom(room.ID, player); err != nil {
|
||||
log.Errorf("[home] JoinNewRoom (PVE) error player %d room %d: %v\n", player.ID, room.ID, err)
|
||||
if err := lobby.JoinRoom(room.ID, player); err != nil {
|
||||
log.Errorf("[home] JoinRoom (PVE) error player %d room %d: %v\n", player.ID, room.ID, err)
|
||||
return consts.StateHome, nil
|
||||
}
|
||||
|
||||
@@ -110,12 +110,12 @@ func handleCreatePveRoom(player *lobby.Player, req *protocol.Request) (consts.St
|
||||
func handleJoinRoom(player *lobby.Player, req *protocol.Request) (consts.StateID, error) {
|
||||
roomID := int64(req.GetJoinRoom().GetRoomId())
|
||||
|
||||
err := lobby.JoinNewRoom(roomID, player)
|
||||
err := lobby.JoinRoom(roomID, player)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case lobby.ErrRoomFull, lobby.ErrRoomPlaying:
|
||||
owner := ""
|
||||
if r, ok := lobby.GetNewRoom(roomID); ok {
|
||||
if r, ok := lobby.GetRoom(roomID); ok {
|
||||
owner = r.OwnerNickname
|
||||
}
|
||||
_ = player.Send(&protocol.Response{
|
||||
@@ -138,7 +138,7 @@ func handleJoinRoom(player *lobby.Player, req *protocol.Request) (consts.StateID
|
||||
return consts.StateHome, nil
|
||||
}
|
||||
|
||||
room, ok := lobby.GetNewRoom(roomID)
|
||||
room, ok := lobby.GetRoom(roomID)
|
||||
if !ok {
|
||||
return consts.StateHome, nil
|
||||
}
|
||||
@@ -170,7 +170,7 @@ func handleJoinRoom(player *lobby.Player, req *protocol.Request) (consts.StateID
|
||||
func handleWatchGame(player *lobby.Player, req *protocol.Request) (consts.StateID, error) {
|
||||
roomID := int64(req.GetWatchGame().GetRoomId())
|
||||
|
||||
room, ok := lobby.GetNewRoom(roomID)
|
||||
room, ok := lobby.GetRoom(roomID)
|
||||
if !ok {
|
||||
_ = player.Send(&protocol.Response{
|
||||
Payload: &protocol.Response_RoomPlayFailNotFound{
|
||||
@@ -180,7 +180,7 @@ func handleWatchGame(player *lobby.Player, req *protocol.Request) (consts.StateI
|
||||
return consts.StateHome, nil
|
||||
}
|
||||
|
||||
if err := lobby.WatchNewRoom(roomID, player); err != nil {
|
||||
if err := lobby.WatchRoom(roomID, player); err != nil {
|
||||
_ = player.Send(&protocol.Response{
|
||||
Payload: &protocol.Response_RoomPlayFailNotFound{
|
||||
RoomPlayFailNotFound: &protocol.RoomPlayFailNotFoundResponse{},
|
||||
|
||||
@@ -49,7 +49,7 @@ func Run(player *lobby.Player) {
|
||||
defer func() {
|
||||
// Cleanup: remove from room if still in one.
|
||||
if player.RoomID != 0 {
|
||||
lobby.LeaveNewRoom(player)
|
||||
lobby.LeaveRoom(player)
|
||||
}
|
||||
lobby.RemovePlayer(player.ID)
|
||||
log.Infof("[state] player %d state machine exited\n", player.ID)
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
type waitingState struct{}
|
||||
|
||||
func (*waitingState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
room, ok := lobby.GetNewRoom(player.RoomID)
|
||||
room, ok := lobby.GetRoom(player.RoomID)
|
||||
if !ok {
|
||||
log.Errorf("[waiting] player %d: room %d not found\n", player.ID, player.RoomID)
|
||||
return consts.StateHome, nil
|
||||
@@ -37,7 +37,7 @@ func (*waitingState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
}
|
||||
|
||||
// ownerWait loops until the owner triggers a valid GameStartingRequest or exits.
|
||||
func ownerWait(player *lobby.Player, room *lobby.NewRoom) (consts.StateID, error) {
|
||||
func ownerWait(player *lobby.Player, room *lobby.Room) (consts.StateID, error) {
|
||||
for {
|
||||
req, ok := <-player.CmdCh
|
||||
if !ok {
|
||||
@@ -92,7 +92,7 @@ func ownerWait(player *lobby.Player, room *lobby.NewRoom) (consts.StateID, error
|
||||
}
|
||||
|
||||
// joinerWait blocks until the owner starts the game (via StartCh) or the joiner exits.
|
||||
func joinerWait(player *lobby.Player, room *lobby.NewRoom) (consts.StateID, error) {
|
||||
func joinerWait(player *lobby.Player, room *lobby.Room) (consts.StateID, error) {
|
||||
room.RLock()
|
||||
startCh := room.StartCh
|
||||
room.RUnlock()
|
||||
@@ -134,7 +134,7 @@ func joinerWait(player *lobby.Player, room *lobby.NewRoom) (consts.StateID, erro
|
||||
|
||||
// assignColors randomly assigns Black/White to the two players in a PVP room.
|
||||
// Caller must NOT hold room.Lock() before calling — acquires it internally.
|
||||
func assignColors(room *lobby.NewRoom) {
|
||||
func assignColors(room *lobby.Room) {
|
||||
room.Lock()
|
||||
playerIDs := make([]int64, 0, 2)
|
||||
for id := range room.Players {
|
||||
@@ -159,9 +159,9 @@ func assignColors(room *lobby.NewRoom) {
|
||||
|
||||
// leaveRoom removes player from their current room cleanly.
|
||||
// Notifies remaining players via ClientExitResponse.
|
||||
func leaveRoom(player *lobby.Player, room *lobby.NewRoom) {
|
||||
func leaveRoom(player *lobby.Player, room *lobby.Room) {
|
||||
roomID := room.ID
|
||||
lobby.LeaveNewRoom(player)
|
||||
lobby.LeaveRoom(player)
|
||||
|
||||
room.RLock()
|
||||
targets := make([]*lobby.Player, 0, len(room.Players))
|
||||
|
||||
@@ -20,7 +20,7 @@ func makeRegisteredPlayer(t *testing.T, name string) *lobby.Player {
|
||||
}
|
||||
|
||||
// setupPvpRoomWithOwner creates a PVP room with owner already joined.
|
||||
func setupPvpRoomWithOwner(t *testing.T) (*lobby.Player, *lobby.NewRoom) {
|
||||
func setupPvpRoomWithOwner(t *testing.T) (*lobby.Player, *lobby.Room) {
|
||||
t.Helper()
|
||||
owner := makeRegisteredPlayer(t, "Owner")
|
||||
|
||||
@@ -28,8 +28,8 @@ func setupPvpRoomWithOwner(t *testing.T) (*lobby.Player, *lobby.NewRoom) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePvpRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinNewRoom owner: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinRoom owner: %v", err)
|
||||
}
|
||||
return owner, room
|
||||
}
|
||||
@@ -78,8 +78,8 @@ func TestWaitingOwnerGameStartingRequiresFullRoom(t *testing.T) {
|
||||
func TestWaitingOwnerStartsWhenFull(t *testing.T) {
|
||||
owner, room := setupPvpRoomWithOwner(t)
|
||||
joiner := makeRegisteredPlayer(t, "Joiner")
|
||||
if err := lobby.JoinNewRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinNewRoom joiner: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinRoom joiner: %v", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
@@ -118,8 +118,8 @@ func TestWaitingOwnerStartsWhenFull(t *testing.T) {
|
||||
func TestWaitingJoinerTransitionsOnStartCh(t *testing.T) {
|
||||
owner, room := setupPvpRoomWithOwner(t)
|
||||
joiner := makeRegisteredPlayer(t, "Joiner")
|
||||
if err := lobby.JoinNewRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinNewRoom joiner: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinRoom joiner: %v", err)
|
||||
}
|
||||
|
||||
// Simulate owner triggering start: close StartCh and mark room playing.
|
||||
|
||||
@@ -25,13 +25,13 @@ func (*watchingState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
req, ok := <-player.CmdCh
|
||||
if !ok {
|
||||
// Connection closed or channel drained — clean up quietly.
|
||||
lobby.UnwatchNewRoom(player)
|
||||
lobby.UnwatchRoom(player)
|
||||
return 0, ErrClientExit
|
||||
}
|
||||
|
||||
switch req.Payload.(type) {
|
||||
case *protocol.Request_WatchGameExit:
|
||||
lobby.UnwatchNewRoom(player)
|
||||
lobby.UnwatchRoom(player)
|
||||
_ = player.Send(&protocol.Response{
|
||||
Payload: &protocol.Response_ShowOptions{
|
||||
ShowOptions: &protocol.ShowOptionsResponse{},
|
||||
@@ -40,7 +40,7 @@ func (*watchingState) Next(player *lobby.Player) (consts.StateID, error) {
|
||||
return consts.StateHome, nil
|
||||
|
||||
case *protocol.Request_ClientExit:
|
||||
lobby.UnwatchNewRoom(player)
|
||||
lobby.UnwatchRoom(player)
|
||||
return 0, ErrClientExit
|
||||
|
||||
case *protocol.Request_GameMove:
|
||||
|
||||
@@ -12,24 +12,24 @@ import (
|
||||
|
||||
// setupWatchingPlayer creates a room with two players, then adds a spectator
|
||||
// and returns the spectator player + the room.
|
||||
func setupWatchingPlayer(t *testing.T) (*lobby.Player, *lobby.NewRoom) {
|
||||
func setupWatchingPlayer(t *testing.T) (*lobby.Player, *lobby.Room) {
|
||||
t.Helper()
|
||||
owner := makeRegisteredPlayer(t, "Black")
|
||||
room, err := lobby.CreatePvpRoom(owner)
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePvpRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinNewRoom owner: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinRoom owner: %v", err)
|
||||
}
|
||||
joiner := makeRegisteredPlayer(t, "White")
|
||||
if err := lobby.JoinNewRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinNewRoom joiner: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinRoom joiner: %v", err)
|
||||
}
|
||||
|
||||
spectator := makeRegisteredPlayer(t, "Watcher")
|
||||
if err := lobby.WatchNewRoom(room.ID, spectator); err != nil {
|
||||
t.Fatalf("WatchNewRoom: %v", err)
|
||||
if err := lobby.WatchRoom(room.ID, spectator); err != nil {
|
||||
t.Fatalf("WatchRoom: %v", err)
|
||||
}
|
||||
return spectator, room
|
||||
}
|
||||
@@ -178,12 +178,12 @@ func TestSnapshot_SendsOwnerStartingAndHistory(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePvpRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinNewRoom owner: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinRoom owner: %v", err)
|
||||
}
|
||||
joiner := makeRegisteredPlayer(t, "White")
|
||||
if err := lobby.JoinNewRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinNewRoom joiner: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinRoom joiner: %v", err)
|
||||
}
|
||||
|
||||
// Set up color assignment and some move history.
|
||||
@@ -238,8 +238,8 @@ func TestHomeWatchGame_RouteToWatching(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePvpRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinNewRoom: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinRoom: %v", err)
|
||||
}
|
||||
|
||||
spectator := makeRegisteredPlayer(t, "Spec")
|
||||
@@ -315,17 +315,17 @@ func TestDeleteRoom_EjectsSpectators(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePvpRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinNewRoom: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinRoom: %v", err)
|
||||
}
|
||||
|
||||
spectator := makeRegisteredPlayer(t, "Watcher")
|
||||
if err := lobby.WatchNewRoom(room.ID, spectator); err != nil {
|
||||
t.Fatalf("WatchNewRoom: %v", err)
|
||||
if err := lobby.WatchRoom(room.ID, spectator); err != nil {
|
||||
t.Fatalf("WatchRoom: %v", err)
|
||||
}
|
||||
|
||||
// Owner leaves — should eject spectator.
|
||||
lobby.LeaveNewRoom(owner)
|
||||
lobby.LeaveRoom(owner)
|
||||
|
||||
// Give async sends a moment to land.
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
@@ -361,16 +361,16 @@ func TestRoomSummary_IncludesSpectators(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("CreatePvpRoom: %v", err)
|
||||
}
|
||||
if err := lobby.JoinNewRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinNewRoom: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, owner); err != nil {
|
||||
t.Fatalf("JoinRoom: %v", err)
|
||||
}
|
||||
joiner := makeRegisteredPlayer(t, "Joiner")
|
||||
if err := lobby.JoinNewRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinNewRoom joiner: %v", err)
|
||||
if err := lobby.JoinRoom(room.ID, joiner); err != nil {
|
||||
t.Fatalf("JoinRoom joiner: %v", err)
|
||||
}
|
||||
spectator := makeRegisteredPlayer(t, "Watcher")
|
||||
if err := lobby.WatchNewRoom(room.ID, spectator); err != nil {
|
||||
t.Fatalf("WatchNewRoom: %v", err)
|
||||
if err := lobby.WatchRoom(room.ID, spectator); err != nil {
|
||||
t.Fatalf("WatchRoom: %v", err)
|
||||
}
|
||||
|
||||
room.RLock()
|
||||
|
||||
Reference in New Issue
Block a user