aboutsummaryrefslogtreecommitdiffhomepage
path: root/backend
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2024-07-28 19:42:05 +0900
committernsfisis <nsfisis@gmail.com>2024-07-28 19:52:31 +0900
commit22ddf340f0b0c8d0cd04c34d9fa1481a1fbf422f (patch)
treefab36f8dc1a2be23e331752b3e3d35e10d797ecf /backend
parent7bd55ee264f7eefda6c1f71865a2c6287d7e20fa (diff)
downloadphperkaigi-2025-albatross-22ddf340f0b0c8d0cd04c34d9fa1481a1fbf422f.tar.gz
phperkaigi-2025-albatross-22ddf340f0b0c8d0cd04c34d9fa1481a1fbf422f.tar.zst
phperkaigi-2025-albatross-22ddf340f0b0c8d0cd04c34d9fa1481a1fbf422f.zip
refactor(backend): move game-related code to game module
Diffstat (limited to 'backend')
-rw-r--r--backend/game/game.go (renamed from backend/game.go)151
-rw-r--r--backend/game/http.go56
-rw-r--r--backend/game/message.go (renamed from backend/message.go)2
-rw-r--r--backend/game/ws.go46
-rw-r--r--backend/main.go131
5 files changed, 226 insertions, 160 deletions
diff --git a/backend/game.go b/backend/game/game.go
index a8f688e..9e63a1e 100644
--- a/backend/game.go
+++ b/backend/game/game.go
@@ -1,15 +1,86 @@
-package main
+package game
import (
+ "context"
"log"
- "net/http"
"time"
"github.com/gorilla/websocket"
+
+ "github.com/nsfisis/iosdc-2024-albatross/backend/api"
+ "github.com/nsfisis/iosdc-2024-albatross/backend/db"
+)
+
+type gameState = api.GameState
+
+const (
+ gameStateClosed gameState = api.Closed
+ gameStateWaitingEntries gameState = api.WaitingEntries
+ gameStateWaitingStart gameState = api.WaitingStart
+ gameStatePrepare gameState = api.Prepare
+ gameStateStarting gameState = api.Starting
+ gameStateGaming gameState = api.Gaming
+ gameStateFinished gameState = api.Finished
)
+type game struct {
+ gameID int
+ state string
+ displayName string
+ durationSeconds int
+ startedAt *time.Time
+ problem *problem
+}
+
+type problem struct {
+ problemID int
+ title string
+ description string
+}
+
+// func startGame(game *Game) {
+// if gameHubs[game.GameID] != nil {
+// return
+// }
+// gameHubs[game.GameID] = NewGameHub(game)
+// go gameHubs[game.GameID].Run()
+// }
+
+/*
+func handleGolfPost(w http.ResponseWriter, r *http.Request) {
+ var yourTeam string
+ waitingGolfGames := []Game{}
+ err := db.Select(&waitingGolfGames, "SELECT * FROM games WHERE type = $1 AND state = $2 ORDER BY created_at", gameTypeGolf, gameStateWaiting)
+ if err != nil {
+ http.Error(w, "Error getting games", http.StatusInternalServerError)
+ return
+ }
+ if len(waitingGolfGames) == 0 {
+ _, err = db.Exec("INSERT INTO games (type, state) VALUES ($1, $2)", gameTypeGolf, gameStateWaiting)
+ if err != nil {
+ http.Error(w, "Error creating game", http.StatusInternalServerError)
+ return
+ }
+ waitingGolfGames = []Game{}
+ err = db.Select(&waitingGolfGames, "SELECT * FROM games WHERE type = $1 AND state = $2 ORDER BY created_at", gameTypeGolf, gameStateWaiting)
+ if err != nil {
+ http.Error(w, "Error getting games", http.StatusInternalServerError)
+ return
+ }
+ yourTeam = "a"
+ startGame(&waitingGolfGames[0])
+ } else {
+ yourTeam = "b"
+ db.Exec("UPDATE games SET state = $1 WHERE game_id = $2", gameStateReady, waitingGolfGames[0].GameID)
+ }
+ waitingGame := waitingGolfGames[0]
+
+ http.Redirect(w, r, fmt.Sprintf("/golf/%d/%s/", waitingGame.GameID, yourTeam), http.StatusSeeOther)
+}
+*/
+
type GameHub struct {
- game *Game
+ game *game
clients map[*GameClient]bool
receive chan *MessageWithClient
register chan *GameClient
@@ -21,7 +92,7 @@ type GameHub struct {
finishTime time.Time
}
-func NewGameHub(game *Game) *GameHub {
+func NewGameHub(game *game) *GameHub {
return &GameHub{
game: game,
clients: make(map[*GameClient]bool),
@@ -181,23 +252,6 @@ func (h *GameHub) closeWatcher(watcher *GameWatcher) {
close(watcher.send)
}
-const (
- writeWait = 10 * time.Second
- pongWait = 60 * time.Second
- pingPeriod = (pongWait * 9) / 10
- maxMessageSize = 512
-)
-
-var (
- newline = []byte{'\n'}
- space = []byte{' '}
-)
-
-var upgrader = websocket.Upgrader{
- ReadBufferSize: 1024,
- WriteBufferSize: 1024,
-}
-
type GameClient struct {
hub *GameHub
conn *websocket.Conn
@@ -263,32 +317,6 @@ func (c *GameClient) writePump() {
}
}
-func serveWs(hub *GameHub, w http.ResponseWriter, r *http.Request, team string) error {
- conn, err := upgrader.Upgrade(w, r, nil)
- if err != nil {
- return err
- }
- client := &GameClient{hub: hub, conn: conn, send: make(chan *Message), team: team}
- client.hub.register <- client
-
- go client.writePump()
- go client.readPump()
- return nil
-}
-
-func serveWsWatcher(hub *GameHub, w http.ResponseWriter, r *http.Request) error {
- conn, err := upgrader.Upgrade(w, r, nil)
- if err != nil {
- return err
- }
- watcher := &GameWatcher{hub: hub, conn: conn, send: make(chan *Message)}
- watcher.hub.registerWatcher <- watcher
-
- go watcher.writePump()
- go watcher.readPump()
- return nil
-}
-
// Receives messages from the client and sends them to the hub.
func (c *GameWatcher) readPump() {
c.conn.SetReadLimit(maxMessageSize)
@@ -326,3 +354,32 @@ func (c *GameWatcher) writePump() {
}
}
}
+
+type GameHubs struct {
+ hubs map[int]*GameHub
+}
+
+func NewGameHubs() *GameHubs {
+ return &GameHubs{
+ hubs: make(map[int]*GameHub),
+ }
+}
+
+func (hubs *GameHubs) Close() {
+ for _, hub := range hubs.hubs {
+ hub.Close()
+ }
+}
+
+func (hubs *GameHubs) RestoreFromDB(ctx context.Context, q *db.Queries) error {
+ games, err := q.ListGames(ctx)
+ if err != nil {
+ return err
+ }
+ _ = games
+ return nil
+}
+
+func (hubs *GameHubs) SockHandler() *sockHandler {
+ return newSockHandler(hubs)
+}
diff --git a/backend/game/http.go b/backend/game/http.go
new file mode 100644
index 0000000..a5a7ded
--- /dev/null
+++ b/backend/game/http.go
@@ -0,0 +1,56 @@
+package game
+
+import (
+ "net/http"
+ "strconv"
+
+ "github.com/labstack/echo/v4"
+)
+
+type sockHandler struct {
+ hubs *GameHubs
+}
+
+func newSockHandler(hubs *GameHubs) *sockHandler {
+ return &sockHandler{
+ hubs: hubs,
+ }
+}
+
+func (h *sockHandler) HandleSockGolfPlay(c echo.Context) error {
+ gameId := c.Param("gameId")
+ gameIdInt, err := strconv.Atoi(gameId)
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, "Invalid game id")
+ }
+ var foundHub *GameHub
+ for _, hub := range h.hubs.hubs {
+ if hub.game.gameID == gameIdInt {
+ foundHub = hub
+ break
+ }
+ }
+ if foundHub == nil {
+ return echo.NewHTTPError(http.StatusNotFound, "Game not found")
+ }
+ return servePlayerWs(foundHub, c.Response(), c.Request(), "a")
+}
+
+func (h *sockHandler) HandleSockGolfWatch(c echo.Context) error {
+ gameId := c.Param("gameId")
+ gameIdInt, err := strconv.Atoi(gameId)
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, "Invalid game id")
+ }
+ var foundHub *GameHub
+ for _, hub := range h.hubs.hubs {
+ if hub.game.gameID == gameIdInt {
+ foundHub = hub
+ break
+ }
+ }
+ if foundHub == nil {
+ return echo.NewHTTPError(http.StatusNotFound, "Game not found")
+ }
+ return serveWatcherWs(foundHub, c.Response(), c.Request())
+}
diff --git a/backend/message.go b/backend/game/message.go
index f466a8f..7d1a166 100644
--- a/backend/message.go
+++ b/backend/game/message.go
@@ -1,4 +1,4 @@
-package main
+package game
import (
"encoding/json"
diff --git a/backend/game/ws.go b/backend/game/ws.go
new file mode 100644
index 0000000..2ed17af
--- /dev/null
+++ b/backend/game/ws.go
@@ -0,0 +1,46 @@
+package game
+
+import (
+ "net/http"
+ "time"
+
+ "github.com/gorilla/websocket"
+)
+
+const (
+ writeWait = 10 * time.Second
+ pongWait = 60 * time.Second
+ pingPeriod = (pongWait * 9) / 10
+ maxMessageSize = 512
+)
+
+var upgrader = websocket.Upgrader{
+ ReadBufferSize: 1024,
+ WriteBufferSize: 1024,
+}
+
+func servePlayerWs(hub *GameHub, w http.ResponseWriter, r *http.Request, team string) error {
+ conn, err := upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ return err
+ }
+ client := &GameClient{hub: hub, conn: conn, send: make(chan *Message), team: team}
+ client.hub.register <- client
+
+ go client.writePump()
+ go client.readPump()
+ return nil
+}
+
+func serveWatcherWs(hub *GameHub, w http.ResponseWriter, r *http.Request) error {
+ conn, err := upgrader.Upgrade(w, r, nil)
+ if err != nil {
+ return err
+ }
+ watcher := &GameWatcher{hub: hub, conn: conn, send: make(chan *Message)}
+ watcher.hub.registerWatcher <- watcher
+
+ go watcher.writePump()
+ go watcher.readPump()
+ return nil
+}
diff --git a/backend/main.go b/backend/main.go
index 46c7ed8..379fe8d 100644
--- a/backend/main.go
+++ b/backend/main.go
@@ -5,7 +5,6 @@ import (
"fmt"
"log"
"net/http"
- "strconv"
"github.com/jackc/pgx/v5"
"github.com/labstack/echo/v4"
@@ -14,70 +13,9 @@ import (
"github.com/nsfisis/iosdc-2024-albatross/backend/api"
"github.com/nsfisis/iosdc-2024-albatross/backend/db"
+ "github.com/nsfisis/iosdc-2024-albatross/backend/game"
)
-const (
- gameTypeGolf = "golf"
-)
-
-const (
- gameStateWaiting = "waiting"
- gameStateReady = "ready"
- gameStatePlaying = "playing"
- gameStateFinished = "finished"
-)
-
-type Game struct {
- GameID int `db:"game_id"`
- // "golf"
- Type string `db:"type"`
- CreatedAt string `db:"created_at"`
- State string `db:"state"`
-}
-
-var gameHubs = map[int]*GameHub{}
-
-func startGame(game *Game) {
- if gameHubs[game.GameID] != nil {
- return
- }
- gameHubs[game.GameID] = NewGameHub(game)
- go gameHubs[game.GameID].Run()
-}
-
-/*
-func handleGolfPost(w http.ResponseWriter, r *http.Request) {
- var yourTeam string
- waitingGolfGames := []Game{}
- err := db.Select(&waitingGolfGames, "SELECT * FROM games WHERE type = $1 AND state = $2 ORDER BY created_at", gameTypeGolf, gameStateWaiting)
- if err != nil {
- http.Error(w, "Error getting games", http.StatusInternalServerError)
- return
- }
- if len(waitingGolfGames) == 0 {
- _, err = db.Exec("INSERT INTO games (type, state) VALUES ($1, $2)", gameTypeGolf, gameStateWaiting)
- if err != nil {
- http.Error(w, "Error creating game", http.StatusInternalServerError)
- return
- }
- waitingGolfGames = []Game{}
- err = db.Select(&waitingGolfGames, "SELECT * FROM games WHERE type = $1 AND state = $2 ORDER BY created_at", gameTypeGolf, gameStateWaiting)
- if err != nil {
- http.Error(w, "Error getting games", http.StatusInternalServerError)
- return
- }
- yourTeam = "a"
- startGame(&waitingGolfGames[0])
- } else {
- yourTeam = "b"
- db.Exec("UPDATE games SET state = $1 WHERE game_id = $2", gameStateReady, waitingGolfGames[0].GameID)
- }
- waitingGame := waitingGolfGames[0]
-
- http.Redirect(w, r, fmt.Sprintf("/golf/%d/%s/", waitingGame.GameID, yourTeam), http.StatusSeeOther)
-}
-*/
-
func main() {
var err error
config, err := NewConfigFromEnv()
@@ -105,59 +43,28 @@ func main() {
e.Use(middleware.Logger())
e.Use(middleware.Recover())
- {
- apiGroup := e.Group("/api")
- apiGroup.Use(oapimiddleware.OapiRequestValidator(openApiSpec))
- apiHandler := api.NewHandler(queries)
- api.RegisterHandlers(apiGroup, api.NewStrictHandler(apiHandler, []api.StrictMiddlewareFunc{
- api.NewJWTMiddleware(),
- }))
- }
+ apiGroup := e.Group("/api")
+ apiGroup.Use(oapimiddleware.OapiRequestValidator(openApiSpec))
+ apiHandler := api.NewHandler(queries)
+ api.RegisterHandlers(apiGroup, api.NewStrictHandler(apiHandler, []api.StrictMiddlewareFunc{
+ api.NewJWTMiddleware(),
+ }))
- e.GET("/sock/golf/:gameId/watch", func(c echo.Context) error {
- gameId := c.Param("gameId")
- gameIdInt, err := strconv.Atoi(gameId)
- if err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, "Invalid game id")
- }
- var hub *GameHub
- for _, h := range gameHubs {
- if h.game.GameID == gameIdInt {
- hub = h
- break
- }
- }
- if hub == nil {
- return echo.NewHTTPError(http.StatusNotFound, "Game not found")
- }
- return serveWsWatcher(hub, c.Response(), c.Request())
+ gameHubs := game.NewGameHubs()
+ err = gameHubs.RestoreFromDB(ctx, queries)
+ if err != nil {
+ log.Fatalf("Error restoring game hubs from db %v", err)
+ }
+ defer gameHubs.Close()
+ sockGroup := e.Group("/sock")
+ sockHandler := gameHubs.SockHandler()
+ sockGroup.GET("/golf/:gameId/watch", func(c echo.Context) error {
+ return sockHandler.HandleSockGolfWatch(c)
})
-
- e.GET("/sock/golf/:gameId/play", func(c echo.Context) error {
- gameId := c.Param("gameId")
- gameIdInt, err := strconv.Atoi(gameId)
- if err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, "Invalid game id")
- }
- var hub *GameHub
- for _, h := range gameHubs {
- if h.game.GameID == gameIdInt {
- hub = h
- break
- }
- }
- if hub == nil {
- return echo.NewHTTPError(http.StatusNotFound, "Game not found")
- }
- return serveWs(hub, c.Response(), c.Request(), "a")
+ sockGroup.GET("/golf/:gameId/play", func(c echo.Context) error {
+ return sockHandler.HandleSockGolfPlay(c)
})
- defer func() {
- for _, hub := range gameHubs {
- hub.Close()
- }
- }()
-
if err := e.Start(":80"); err != http.ErrServerClosed {
log.Fatal(err)
}