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