refactor: extract shared player, session, and chat packages #5
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/ryanhamamura/c4/features/c4game/components"
|
||||
"github.com/ryanhamamura/c4/features/c4game/pages"
|
||||
"github.com/ryanhamamura/c4/game"
|
||||
"github.com/ryanhamamura/c4/player"
|
||||
)
|
||||
|
||||
func HandleGamePage(store *game.GameStore, sessions *scs.SessionManager, queries *repository.Queries) http.HandlerFunc {
|
||||
@@ -30,15 +31,15 @@ func HandleGamePage(store *game.GameStore, sessions *scs.SessionManager, queries
|
||||
return
|
||||
}
|
||||
|
||||
playerID := game.PlayerID(sessions.GetString(r.Context(), "player_id"))
|
||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
||||
if playerID == "" {
|
||||
playerID = game.PlayerID(game.GenerateID(8))
|
||||
playerID = player.ID(player.GenerateID(8))
|
||||
sessions.Put(r.Context(), "player_id", string(playerID))
|
||||
}
|
||||
|
||||
userID := sessions.GetString(r.Context(), "user_id")
|
||||
if userID != "" {
|
||||
playerID = game.PlayerID(userID)
|
||||
playerID = player.ID(userID)
|
||||
}
|
||||
|
||||
nickname := sessions.GetString(r.Context(), "nickname")
|
||||
@@ -95,10 +96,10 @@ func HandleGameEvents(store *game.GameStore, nc *nats.Conn, sessions *scs.Sessio
|
||||
return
|
||||
}
|
||||
|
||||
playerID := game.PlayerID(sessions.GetString(r.Context(), "player_id"))
|
||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
||||
userID := sessions.GetString(r.Context(), "user_id")
|
||||
if userID != "" {
|
||||
playerID = game.PlayerID(userID)
|
||||
playerID = player.ID(userID)
|
||||
}
|
||||
|
||||
myColor := gi.GetPlayerColor(playerID)
|
||||
@@ -185,10 +186,10 @@ func HandleDropPiece(store *game.GameStore, sessions *scs.SessionManager) http.H
|
||||
return
|
||||
}
|
||||
|
||||
playerID := game.PlayerID(sessions.GetString(r.Context(), "player_id"))
|
||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
||||
userID := sessions.GetString(r.Context(), "user_id")
|
||||
if userID != "" {
|
||||
playerID = game.PlayerID(userID)
|
||||
playerID = player.ID(userID)
|
||||
}
|
||||
|
||||
myColor := gi.GetPlayerColor(playerID)
|
||||
@@ -229,10 +230,10 @@ func HandleSendChat(store *game.GameStore, nc *nats.Conn, sessions *scs.SessionM
|
||||
return
|
||||
}
|
||||
|
||||
playerID := game.PlayerID(sessions.GetString(r.Context(), "player_id"))
|
||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
||||
userID := sessions.GetString(r.Context(), "user_id")
|
||||
if userID != "" {
|
||||
playerID = game.PlayerID(userID)
|
||||
playerID = player.ID(userID)
|
||||
}
|
||||
|
||||
myColor := gi.GetPlayerColor(playerID)
|
||||
@@ -298,10 +299,10 @@ func HandleSetNickname(store *game.GameStore, sessions *scs.SessionManager) http
|
||||
|
||||
sessions.Put(r.Context(), "nickname", signals.Nickname)
|
||||
|
||||
playerID := game.PlayerID(sessions.GetString(r.Context(), "player_id"))
|
||||
playerID := player.ID(sessions.GetString(r.Context(), "player_id"))
|
||||
userID := sessions.GetString(r.Context(), "user_id")
|
||||
if userID != "" {
|
||||
playerID = game.PlayerID(userID)
|
||||
playerID = player.ID(userID)
|
||||
}
|
||||
|
||||
if gi.GetPlayerColor(playerID) == 0 {
|
||||
|
||||
@@ -13,21 +13,21 @@ import (
|
||||
|
||||
"github.com/ryanhamamura/c4/features/snakegame/components"
|
||||
"github.com/ryanhamamura/c4/features/snakegame/pages"
|
||||
"github.com/ryanhamamura/c4/game"
|
||||
"github.com/ryanhamamura/c4/player"
|
||||
"github.com/ryanhamamura/c4/snake"
|
||||
)
|
||||
|
||||
func getPlayerID(sessions *scs.SessionManager, r *http.Request) snake.PlayerID {
|
||||
func getPlayerID(sessions *scs.SessionManager, r *http.Request) player.ID {
|
||||
pid := sessions.GetString(r.Context(), "player_id")
|
||||
if pid == "" {
|
||||
pid = game.GenerateID(8)
|
||||
pid = player.GenerateID(8)
|
||||
sessions.Put(r.Context(), "player_id", pid)
|
||||
}
|
||||
userID := sessions.GetString(r.Context(), "user_id")
|
||||
if userID != "" {
|
||||
return snake.PlayerID(userID)
|
||||
return player.ID(userID)
|
||||
}
|
||||
return snake.PlayerID(pid)
|
||||
return player.ID(pid)
|
||||
}
|
||||
|
||||
func HandleSnakePage(snakeStore *snake.SnakeStore, sessions *scs.SessionManager) http.HandlerFunc {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/ryanhamamura/c4/db/repository"
|
||||
"github.com/ryanhamamura/c4/player"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -109,19 +110,19 @@ func gameFromRow(row *repository.Game) (*Game, error) {
|
||||
func playersFromRows(rows []*repository.GamePlayer) []*Player {
|
||||
players := make([]*Player, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
player := &Player{
|
||||
p := &Player{
|
||||
Nickname: row.Nickname,
|
||||
Color: int(row.Color),
|
||||
}
|
||||
|
||||
if row.UserID != nil {
|
||||
player.UserID = row.UserID
|
||||
player.ID = PlayerID(*row.UserID)
|
||||
p.UserID = row.UserID
|
||||
p.ID = player.ID(*row.UserID)
|
||||
} else if row.GuestPlayerID != nil {
|
||||
player.ID = PlayerID(*row.GuestPlayerID)
|
||||
p.ID = player.ID(*row.GuestPlayerID)
|
||||
}
|
||||
|
||||
players = append(players, player)
|
||||
players = append(players, p)
|
||||
}
|
||||
return players
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@ package game
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
|
||||
"github.com/ryanhamamura/c4/db/repository"
|
||||
"github.com/ryanhamamura/c4/player"
|
||||
)
|
||||
|
||||
type PlayerSession struct {
|
||||
@@ -40,7 +39,7 @@ func (gs *GameStore) makeNotify(gameID string) func() {
|
||||
}
|
||||
|
||||
func (gs *GameStore) Create() *GameInstance {
|
||||
id := GenerateID(4)
|
||||
id := player.GenerateID(4)
|
||||
gi := NewGameInstance(id)
|
||||
gi.queries = gs.queries
|
||||
gi.notify = gs.makeNotify(id)
|
||||
@@ -107,12 +106,6 @@ func (gs *GameStore) Delete(id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateID(size int) string {
|
||||
b := make([]byte, size)
|
||||
_, _ = rand.Read(b)
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
type GameInstance struct {
|
||||
game *Game
|
||||
gameMu sync.RWMutex
|
||||
@@ -166,7 +159,7 @@ func (gi *GameInstance) GetGame() *Game {
|
||||
return gi.game
|
||||
}
|
||||
|
||||
func (gi *GameInstance) GetPlayerColor(pid PlayerID) int {
|
||||
func (gi *GameInstance) GetPlayerColor(pid player.ID) int {
|
||||
gi.gameMu.RLock()
|
||||
defer gi.gameMu.RUnlock()
|
||||
for _, p := range gi.game.Players {
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package game
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
type PlayerID string
|
||||
"github.com/ryanhamamura/c4/player"
|
||||
)
|
||||
|
||||
type Player struct {
|
||||
ID PlayerID
|
||||
ID player.ID
|
||||
UserID *string // UUID for authenticated users, nil for guests
|
||||
Nickname string
|
||||
Color int // 1 = Red, 2 = Yellow
|
||||
|
||||
18
player/player.go
Normal file
18
player/player.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// Package player provides shared identity types used across game packages.
|
||||
package player
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// ID uniquely identifies a player within a session. For authenticated users
|
||||
// this is their user UUID; for guests it's a random hex string.
|
||||
type ID string
|
||||
|
||||
// GenerateID returns a random hex string of 2*size characters.
|
||||
func GenerateID(size int) string {
|
||||
b := make([]byte, size)
|
||||
_, _ = rand.Read(b)
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/ryanhamamura/c4/db/repository"
|
||||
"github.com/ryanhamamura/c4/player"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -122,19 +123,19 @@ func snakeGameFromRow(row *repository.Game) (*SnakeGame, error) {
|
||||
func snakePlayersFromRows(rows []*repository.GamePlayer) []*Player {
|
||||
players := make([]*Player, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
player := &Player{
|
||||
p := &Player{
|
||||
Nickname: row.Nickname,
|
||||
Slot: int(row.Slot),
|
||||
}
|
||||
|
||||
if row.UserID != nil {
|
||||
player.UserID = row.UserID
|
||||
player.ID = PlayerID(*row.UserID)
|
||||
p.UserID = row.UserID
|
||||
p.ID = player.ID(*row.UserID)
|
||||
} else if row.GuestPlayerID != nil {
|
||||
player.ID = PlayerID(*row.GuestPlayerID)
|
||||
p.ID = player.ID(*row.GuestPlayerID)
|
||||
}
|
||||
|
||||
players = append(players, player)
|
||||
players = append(players, p)
|
||||
}
|
||||
return players
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/ryanhamamura/c4/db/repository"
|
||||
"github.com/ryanhamamura/c4/game"
|
||||
"github.com/ryanhamamura/c4/player"
|
||||
)
|
||||
|
||||
type SnakeStore struct {
|
||||
@@ -38,7 +38,7 @@ func (ss *SnakeStore) Create(width, height int, mode GameMode, speed int) *Snake
|
||||
if speed <= 0 {
|
||||
speed = DefaultSpeed
|
||||
}
|
||||
id := game.GenerateID(4)
|
||||
id := player.GenerateID(4)
|
||||
sg := &SnakeGame{
|
||||
ID: id,
|
||||
State: &GameState{
|
||||
@@ -172,7 +172,7 @@ func (si *SnakeGameInstance) GetGame() *SnakeGame {
|
||||
return si.game.snapshot()
|
||||
}
|
||||
|
||||
func (si *SnakeGameInstance) GetPlayerSlot(pid PlayerID) int {
|
||||
func (si *SnakeGameInstance) GetPlayerSlot(pid player.ID) int {
|
||||
si.gameMu.RLock()
|
||||
defer si.gameMu.RUnlock()
|
||||
for i, p := range si.game.Players {
|
||||
|
||||
@@ -3,6 +3,8 @@ package snake
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/ryanhamamura/c4/player"
|
||||
)
|
||||
|
||||
type Direction int
|
||||
@@ -78,10 +80,8 @@ const (
|
||||
StatusFinished
|
||||
)
|
||||
|
||||
type PlayerID string
|
||||
|
||||
type Player struct {
|
||||
ID PlayerID
|
||||
ID player.ID
|
||||
UserID *string
|
||||
Nickname string
|
||||
Slot int // 0-7
|
||||
|
||||
Reference in New Issue
Block a user