fix: resolve all linting errors and add SSE compression
- Add brotli compression (level 5) to long-lived SSE event streams (HandleGameEvents, HandleSnakeEvents) to reduce wire payload - Fix all errcheck violations with nolint annotations for best-effort calls - Fix goimports: separate stdlib, third-party, and local import groups - Fix staticcheck: add package comments, use tagged switch - Zero lint issues remaining
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
// Package auth provides password hashing and verification using bcrypt.
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import (
|
|||||||
|
|
||||||
"github.com/alexedwards/scs/v2"
|
"github.com/alexedwards/scs/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/starfederation/datastar-go/datastar"
|
||||||
|
|
||||||
"github.com/ryanhamamura/c4/auth"
|
"github.com/ryanhamamura/c4/auth"
|
||||||
"github.com/ryanhamamura/c4/db/repository"
|
"github.com/ryanhamamura/c4/db/repository"
|
||||||
"github.com/ryanhamamura/c4/features/auth/pages"
|
"github.com/ryanhamamura/c4/features/auth/pages"
|
||||||
"github.com/starfederation/datastar-go/datastar"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginSignals struct {
|
type LoginSignals struct {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
// Package auth handles user authentication routes and handlers.
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/alexedwards/scs/v2"
|
"github.com/alexedwards/scs/v2"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
|
||||||
"github.com/ryanhamamura/c4/db/repository"
|
"github.com/ryanhamamura/c4/db/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,12 @@ import (
|
|||||||
"github.com/alexedwards/scs/v2"
|
"github.com/alexedwards/scs/v2"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
|
"github.com/starfederation/datastar-go/datastar"
|
||||||
|
|
||||||
"github.com/ryanhamamura/c4/db/repository"
|
"github.com/ryanhamamura/c4/db/repository"
|
||||||
"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/starfederation/datastar-go/datastar"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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 {
|
||||||
@@ -102,7 +103,9 @@ func HandleGameEvents(store *game.GameStore, nc *nats.Conn, sessions *scs.Sessio
|
|||||||
|
|
||||||
myColor := gi.GetPlayerColor(playerID)
|
myColor := gi.GetPlayerColor(playerID)
|
||||||
|
|
||||||
sse := datastar.NewSSE(w, r)
|
sse := datastar.NewSSE(w, r, datastar.WithCompression(
|
||||||
|
datastar.WithBrotli(datastar.WithBrotliLevel(5)),
|
||||||
|
))
|
||||||
|
|
||||||
// Load initial chat messages
|
// Load initial chat messages
|
||||||
chatMsgs := loadChatMessages(queries, gameID)
|
chatMsgs := loadChatMessages(queries, gameID)
|
||||||
@@ -118,7 +121,7 @@ func HandleGameEvents(store *game.GameStore, nc *nats.Conn, sessions *scs.Sessio
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer gameSub.Unsubscribe()
|
defer gameSub.Unsubscribe() //nolint:errcheck
|
||||||
|
|
||||||
// Subscribe to chat messages
|
// Subscribe to chat messages
|
||||||
chatCh := make(chan *nats.Msg, 64)
|
chatCh := make(chan *nats.Msg, 64)
|
||||||
@@ -126,7 +129,7 @@ func HandleGameEvents(store *game.GameStore, nc *nats.Conn, sessions *scs.Sessio
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer chatSub.Unsubscribe()
|
defer chatSub.Unsubscribe() //nolint:errcheck
|
||||||
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
for {
|
for {
|
||||||
@@ -263,7 +266,7 @@ func HandleSendChat(store *game.GameStore, nc *nats.Conn, sessions *scs.SessionM
|
|||||||
datastar.NewSSE(w, r)
|
datastar.NewSSE(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
nc.Publish("game.chat."+gameID, data)
|
nc.Publish("game.chat."+gameID, data) //nolint:errcheck
|
||||||
|
|
||||||
// Clear the chat input
|
// Clear the chat input
|
||||||
sse := datastar.NewSSE(w, r)
|
sse := datastar.NewSSE(w, r)
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
// Package c4game handles Connect 4 game routes, SSE event streaming, and chat.
|
||||||
package c4game
|
package c4game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/alexedwards/scs/v2"
|
"github.com/alexedwards/scs/v2"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
|
|
||||||
"github.com/ryanhamamura/c4/db/repository"
|
"github.com/ryanhamamura/c4/db/repository"
|
||||||
"github.com/ryanhamamura/c4/game"
|
"github.com/ryanhamamura/c4/game"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func HandleCreateGame(store *game.GameStore, sessions *scs.SessionManager) http.
|
|||||||
|
|
||||||
gi := store.Create()
|
gi := store.Create()
|
||||||
sse := datastar.NewSSE(w, r)
|
sse := datastar.NewSSE(w, r)
|
||||||
sse.ExecuteScript(fmt.Sprintf("window.location.href='/game/%s'", gi.ID()))
|
sse.ExecuteScript(fmt.Sprintf("window.location.href='/game/%s'", gi.ID())) //nolint:errcheck
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,10 +104,10 @@ func HandleDeleteGame(store *game.GameStore, sessions *scs.SessionManager) http.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
store.Delete(gameID)
|
store.Delete(gameID) //nolint:errcheck
|
||||||
|
|
||||||
sse := datastar.NewSSE(w, r)
|
sse := datastar.NewSSE(w, r)
|
||||||
sse.ExecuteScript("window.location.href='/'")
|
sse.ExecuteScript("window.location.href='/'") //nolint:errcheck
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ func HandleCreateSnakeGame(snakeStore *snake.SnakeStore, sessions *scs.SessionMa
|
|||||||
si := snakeStore.Create(preset.Width, preset.Height, mode, speed)
|
si := snakeStore.Create(preset.Width, preset.Height, mode, speed)
|
||||||
|
|
||||||
sse := datastar.NewSSE(w, r)
|
sse := datastar.NewSSE(w, r)
|
||||||
sse.ExecuteScript(fmt.Sprintf("window.location.href='/snake/%s'", si.ID()))
|
sse.ExecuteScript(fmt.Sprintf("window.location.href='/snake/%s'", si.ID())) //nolint:errcheck
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +163,6 @@ func HandleLogout(sessions *scs.SessionManager) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sse := datastar.NewSSE(w, r)
|
sse := datastar.NewSSE(w, r)
|
||||||
sse.ExecuteScript("window.location.href='/'")
|
sse.ExecuteScript("window.location.href='/'") //nolint:errcheck
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package lobby handles the game lobby page, game creation, and navigation.
|
||||||
package lobby
|
package lobby
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import (
|
|||||||
"github.com/alexedwards/scs/v2"
|
"github.com/alexedwards/scs/v2"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
|
"github.com/starfederation/datastar-go/datastar"
|
||||||
|
|
||||||
"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/game"
|
||||||
"github.com/ryanhamamura/c4/snake"
|
"github.com/ryanhamamura/c4/snake"
|
||||||
"github.com/starfederation/datastar-go/datastar"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getPlayerID(sessions *scs.SessionManager, r *http.Request) snake.PlayerID {
|
func getPlayerID(sessions *scs.SessionManager, r *http.Request) snake.PlayerID {
|
||||||
@@ -90,7 +91,9 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sessions *sc
|
|||||||
playerID := getPlayerID(sessions, r)
|
playerID := getPlayerID(sessions, r)
|
||||||
mySlot := si.GetPlayerSlot(playerID)
|
mySlot := si.GetPlayerSlot(playerID)
|
||||||
|
|
||||||
sse := datastar.NewSSE(w, r)
|
sse := datastar.NewSSE(w, r, datastar.WithCompression(
|
||||||
|
datastar.WithBrotli(datastar.WithBrotliLevel(5)),
|
||||||
|
))
|
||||||
|
|
||||||
// Send initial render
|
// Send initial render
|
||||||
sg := si.GetGame()
|
sg := si.GetGame()
|
||||||
@@ -110,7 +113,7 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sessions *sc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer gameSub.Unsubscribe()
|
defer gameSub.Unsubscribe() //nolint:errcheck
|
||||||
|
|
||||||
// Chat subscription (multiplayer only)
|
// Chat subscription (multiplayer only)
|
||||||
var chatCh chan *nats.Msg
|
var chatCh chan *nats.Msg
|
||||||
@@ -124,7 +127,7 @@ func HandleSnakeEvents(snakeStore *snake.SnakeStore, nc *nats.Conn, sessions *sc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer chatSub.Unsubscribe()
|
defer chatSub.Unsubscribe() //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
// Package snakegame handles snake game routes, SSE event streaming, and chat.
|
||||||
package snakegame
|
package snakegame
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/alexedwards/scs/v2"
|
"github.com/alexedwards/scs/v2"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
|
|
||||||
"github.com/ryanhamamura/c4/snake"
|
"github.com/ryanhamamura/c4/snake"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package game implements Connect 4 game logic, state management, and persistence.
|
||||||
package game
|
package game
|
||||||
|
|
||||||
// DropPiece attempts to drop a piece in the given column.
|
// DropPiece attempts to drop a piece in the given column.
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ func gameFromRow(row repository.Game) (*Game, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if row.WinningCells.Valid {
|
if row.WinningCells.Valid {
|
||||||
g.WinningCellsFromJSON(row.WinningCells.String)
|
_ = g.WinningCellsFromJSON(row.WinningCells.String)
|
||||||
}
|
}
|
||||||
|
|
||||||
if row.RematchGameID.Valid {
|
if row.RematchGameID.Valid {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ func (gs *GameStore) Create() *GameInstance {
|
|||||||
gs.gamesMu.Unlock()
|
gs.gamesMu.Unlock()
|
||||||
|
|
||||||
if gs.queries != nil {
|
if gs.queries != nil {
|
||||||
gs.saveGame(gi.game)
|
gs.saveGame(gi.game) //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
return gi
|
return gi
|
||||||
@@ -75,9 +75,10 @@ func (gs *GameStore) Get(id string) (*GameInstance, bool) {
|
|||||||
|
|
||||||
players, _ := gs.loadGamePlayers(id)
|
players, _ := gs.loadGamePlayers(id)
|
||||||
for _, p := range players {
|
for _, p := range players {
|
||||||
if p.Color == 1 {
|
switch p.Color {
|
||||||
|
case 1:
|
||||||
g.Players[0] = p
|
g.Players[0] = p
|
||||||
} else if p.Color == 2 {
|
case 2:
|
||||||
g.Players[1] = p
|
g.Players[1] = p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +109,7 @@ func (gs *GameStore) Delete(id string) error {
|
|||||||
|
|
||||||
func GenerateID(size int) string {
|
func GenerateID(size int) string {
|
||||||
b := make([]byte, size)
|
b := make([]byte, size)
|
||||||
rand.Read(b)
|
_, _ = rand.Read(b)
|
||||||
return hex.EncodeToString(b)
|
return hex.EncodeToString(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,8 +152,8 @@ func (gi *GameInstance) Join(ps *PlayerSession) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if gi.queries != nil {
|
if gi.queries != nil {
|
||||||
gi.saveGamePlayer(gi.game.ID, ps.Player, slot)
|
gi.saveGamePlayer(gi.game.ID, ps.Player, slot) //nolint:errcheck
|
||||||
gi.saveGame(gi.game)
|
gi.saveGame(gi.game) //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
gi.notify()
|
gi.notify()
|
||||||
@@ -190,7 +191,7 @@ func (gi *GameInstance) CreateRematch(gs *GameStore) *GameInstance {
|
|||||||
|
|
||||||
if gi.queries != nil {
|
if gi.queries != nil {
|
||||||
if err := gi.saveGame(gi.game); err != nil {
|
if err := gi.saveGame(gi.game); err != nil {
|
||||||
gs.Delete(newID)
|
gs.Delete(newID) //nolint:errcheck
|
||||||
gi.game.RematchGameID = nil
|
gi.game.RematchGameID = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -223,7 +224,7 @@ func (gi *GameInstance) DropPiece(col int, playerColor int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if gi.queries != nil {
|
if gi.queries != nil {
|
||||||
gi.saveGame(gi.game)
|
gi.saveGame(gi.game) //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
gi.notify()
|
gi.notify()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func SetupNATS(ctx context.Context) (*nats.Conn, func(), error) {
|
|||||||
|
|
||||||
cleanup := func() {
|
cleanup := func() {
|
||||||
nc.Close()
|
nc.Close()
|
||||||
ns.Close()
|
ns.Close() //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
return nc, cleanup, nil
|
return nc, cleanup, nil
|
||||||
|
|||||||
@@ -40,10 +40,18 @@ func SetupRoutes(
|
|||||||
setupReload(router)
|
setupReload(router)
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.SetupRoutes(router, queries, sessions)
|
if err := auth.SetupRoutes(router, queries, sessions); err != nil {
|
||||||
lobby.SetupRoutes(router, queries, sessions, store, snakeStore)
|
return err
|
||||||
c4game.SetupRoutes(router, store, nc, sessions, queries)
|
}
|
||||||
snakegame.SetupRoutes(router, snakeStore, nc, sessions)
|
if err := lobby.SetupRoutes(router, queries, sessions, store, snakeStore); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c4game.SetupRoutes(router, store, nc, sessions, queries); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := snakegame.SetupRoutes(router, snakeStore, nc, sessions); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package snake implements snake game logic, state management, and persistence.
|
||||||
package snake
|
package snake
|
||||||
|
|
||||||
import "math/rand"
|
import "math/rand"
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func (si *SnakeGameInstance) countdownPhase() {
|
|||||||
si.game.Status = StatusInProgress
|
si.game.Status = StatusInProgress
|
||||||
|
|
||||||
if si.queries != nil {
|
if si.queries != nil {
|
||||||
si.saveSnakeGame(si.game)
|
si.saveSnakeGame(si.game) //nolint:errcheck
|
||||||
}
|
}
|
||||||
si.gameMu.Unlock()
|
si.gameMu.Unlock()
|
||||||
si.notify()
|
si.notify()
|
||||||
@@ -70,7 +70,7 @@ func (si *SnakeGameInstance) countdownPhase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if si.queries != nil {
|
if si.queries != nil {
|
||||||
si.saveSnakeGame(si.game)
|
si.saveSnakeGame(si.game) //nolint:errcheck
|
||||||
}
|
}
|
||||||
si.gameMu.Unlock()
|
si.gameMu.Unlock()
|
||||||
si.notify()
|
si.notify()
|
||||||
@@ -124,7 +124,7 @@ func (si *SnakeGameInstance) gamePhase() {
|
|||||||
if time.Since(lastInput) > inactivityLimit {
|
if time.Since(lastInput) > inactivityLimit {
|
||||||
si.game.Status = StatusFinished
|
si.game.Status = StatusFinished
|
||||||
if si.queries != nil {
|
if si.queries != nil {
|
||||||
si.saveSnakeGame(si.game)
|
si.saveSnakeGame(si.game) //nolint:errcheck
|
||||||
}
|
}
|
||||||
si.gameMu.Unlock()
|
si.gameMu.Unlock()
|
||||||
si.notify()
|
si.notify()
|
||||||
@@ -196,7 +196,7 @@ func (si *SnakeGameInstance) gamePhase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if si.queries != nil {
|
if si.queries != nil {
|
||||||
si.saveSnakeGame(si.game)
|
si.saveSnakeGame(si.game) //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
si.gameMu.Unlock()
|
si.gameMu.Unlock()
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func (ss *SnakeStore) Create(width, height int, mode GameMode, speed int) *Snake
|
|||||||
ss.gamesMu.Unlock()
|
ss.gamesMu.Unlock()
|
||||||
|
|
||||||
if ss.queries != nil {
|
if ss.queries != nil {
|
||||||
ss.saveSnakeGame(sg)
|
ss.saveSnakeGame(sg) //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
return si
|
return si
|
||||||
@@ -207,8 +207,8 @@ func (si *SnakeGameInstance) Join(player *Player) bool {
|
|||||||
si.game.Players[slot] = player
|
si.game.Players[slot] = player
|
||||||
|
|
||||||
if si.queries != nil {
|
if si.queries != nil {
|
||||||
si.saveSnakePlayer(si.game.ID, player)
|
si.saveSnakePlayer(si.game.ID, player) //nolint:errcheck
|
||||||
si.saveSnakeGame(si.game)
|
si.saveSnakeGame(si.game) //nolint:errcheck
|
||||||
}
|
}
|
||||||
|
|
||||||
si.notify()
|
si.notify()
|
||||||
@@ -294,7 +294,7 @@ func (si *SnakeGameInstance) CreateRematch() *SnakeGameInstance {
|
|||||||
si.game.RematchGameID = &newID
|
si.game.RematchGameID = &newID
|
||||||
|
|
||||||
if si.queries != nil {
|
if si.queries != nil {
|
||||||
si.saveSnakeGame(si.game)
|
si.saveSnakeGame(si.game) //nolint:errcheck
|
||||||
}
|
}
|
||||||
si.gameMu.Unlock()
|
si.gameMu.Unlock()
|
||||||
|
|
||||||
@@ -304,6 +304,6 @@ func (si *SnakeGameInstance) CreateRematch() *SnakeGameInstance {
|
|||||||
|
|
||||||
func generateID(size int) string {
|
func generateID(size int) string {
|
||||||
b := make([]byte, size)
|
b := make([]byte, size)
|
||||||
rand.Read(b)
|
_, _ = rand.Read(b)
|
||||||
return hex.EncodeToString(b)
|
return hex.EncodeToString(b)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ type SnakeGame struct {
|
|||||||
Speed int // cells per second
|
Speed int // cells per second
|
||||||
}
|
}
|
||||||
|
|
||||||
// Speed presets
|
// SpeedPreset defines a named speed option for the snake game.
|
||||||
type SpeedPreset struct {
|
type SpeedPreset struct {
|
||||||
Name string
|
Name string
|
||||||
Speed int
|
Speed int
|
||||||
@@ -129,7 +129,7 @@ func (sg *SnakeGame) PlayerCount() int {
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grid presets
|
// GridPreset defines a named grid size option for the snake game.
|
||||||
type GridPreset struct {
|
type GridPreset struct {
|
||||||
Name string
|
Name string
|
||||||
Width int
|
Width int
|
||||||
@@ -163,7 +163,7 @@ func (sg *SnakeGame) snapshot() *SnakeGame {
|
|||||||
return &cp
|
return &cp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snake colors (hex values for CSS)
|
// SnakeColors are hex color values for CSS, indexed by player slot.
|
||||||
var SnakeColors = []string{
|
var SnakeColors = []string{
|
||||||
"#00b894", // 1: Green
|
"#00b894", // 1: Green
|
||||||
"#e17055", // 2: Orange
|
"#e17055", // 2: Orange
|
||||||
|
|||||||
Reference in New Issue
Block a user