aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2024-08-05 03:07:30 +0900
committernsfisis <nsfisis@gmail.com>2024-08-05 03:07:30 +0900
commit9ff9c151e5defd9eed5cba3c88bc341b4360d09c (patch)
treebf5b9e721274ffa58df66823d39f9ceadebc9f98
parentff959dadb1f990173b9df3105ccfc96b1c6c092e (diff)
parent498ad23eaaae1e856c71f44ad0bf3912a6d7e5b7 (diff)
downloadiosdc-japan-2024-albatross-9ff9c151e5defd9eed5cba3c88bc341b4360d09c.tar.gz
iosdc-japan-2024-albatross-9ff9c151e5defd9eed5cba3c88bc341b4360d09c.tar.zst
iosdc-japan-2024-albatross-9ff9c151e5defd9eed5cba3c88bc341b4360d09c.zip
Merge branch 'feat/multiplayer-mode'
-rw-r--r--backend/admin/handlers.go4
-rw-r--r--backend/admin/templates/game_edit.html13
-rw-r--r--backend/admin/templates/games.html2
-rw-r--r--backend/api/generated.go72
-rw-r--r--backend/api/handlers.go2
-rw-r--r--backend/db/models.go1
-rw-r--r--backend/db/query.sql.go71
-rw-r--r--backend/fixtures/dev.sql24
-rw-r--r--backend/game/http.go5
-rw-r--r--backend/game/hub.go10
-rw-r--r--backend/game/models.go6
-rw-r--r--backend/query.sql16
-rw-r--r--backend/schema.sql1
-rw-r--r--frontend/app/.server/api/schema.d.ts5
-rw-r--r--frontend/app/routes/dashboard.tsx3
-rw-r--r--frontend/app/routes/golf.$gameId.watch.tsx5
-rw-r--r--openapi.yaml7
17 files changed, 190 insertions, 57 deletions
diff --git a/backend/admin/handlers.go b/backend/admin/handlers.go
index 14523e6..d9a6977 100644
--- a/backend/admin/handlers.go
+++ b/backend/admin/handlers.go
@@ -130,6 +130,7 @@ func (h *AdminHandler) getGames(c echo.Context) error {
}
games[i] = echo.Map{
"GameID": g.GameID,
+ "GameType": g.GameType,
"State": g.State,
"DisplayName": g.DisplayName,
"DurationSeconds": g.DurationSeconds,
@@ -167,6 +168,7 @@ func (h *AdminHandler) getGameEdit(c echo.Context) error {
"Title": "Game Edit",
"Game": echo.Map{
"GameID": row.GameID,
+ "GameType": row.GameType,
"State": row.State,
"DisplayName": row.DisplayName,
"DurationSeconds": row.DurationSeconds,
@@ -190,6 +192,7 @@ func (h *AdminHandler) postGameEdit(c echo.Context) error {
}
}
+ gameType := c.FormValue("game_type")
state := c.FormValue("state")
displayName := c.FormValue("display_name")
durationSeconds, err := strconv.Atoi(c.FormValue("duration_seconds"))
@@ -247,6 +250,7 @@ func (h *AdminHandler) postGameEdit(c echo.Context) error {
err = h.q.UpdateGame(c.Request().Context(), db.UpdateGameParams{
GameID: int32(gameID),
+ GameType: gameType,
State: state,
DisplayName: displayName,
DurationSeconds: int32(durationSeconds),
diff --git a/backend/admin/templates/game_edit.html b/backend/admin/templates/game_edit.html
index 8bc5410..764b577 100644
--- a/backend/admin/templates/game_edit.html
+++ b/backend/admin/templates/game_edit.html
@@ -15,11 +15,18 @@
<input type="text" name="display_name" value="{{ .Game.DisplayName }}" required>
</div>
<div>
+ <label>Game Type</label>
+ <select name="game_type" required>
+ <option value="1v1"{{ if eq .Game.GameType "1v1" }} selected{{ end }}>1v1</option>
+ <option value="multiplayer"{{ if eq .Game.GameType "multiplayer" }} selected{{ end }}>Multiplayer</option>
+ </select>
+ </div>
+ <div>
<label>State</label>
- <select>
+ <select name="state" required>
<option value="closed"{{ if eq .Game.State "closed" }} selected{{ end }}>Closed</option>
- <option value="waiting_entries"{{ if eq .Game.State "waiting_entries" }} selected{{ end }}>WaitingEntries</option>
- <option value="waiting_start"{{ if eq .Game.State "waiting_start" }} selected{{ end }}>WaitingStart</option>
+ <option value="waiting_entries"{{ if eq .Game.State "waiting_entries" }} selected{{ end }}>Waiting Entries</option>
+ <option value="waiting_start"{{ if eq .Game.State "waiting_start" }} selected{{ end }}>Waiting Start</option>
<option value="prepare"{{ if eq .Game.State "prepare" }} selected{{ end }}>Prepare</option>
<option value="starting"{{ if eq .Game.State "starting" }} selected{{ end }}>Starting</option>
<option value="gaming"{{ if eq .Game.State "gaming" }} selected{{ end }}>Gaming</option>
diff --git a/backend/admin/templates/games.html b/backend/admin/templates/games.html
index 244fc94..47dc4a3 100644
--- a/backend/admin/templates/games.html
+++ b/backend/admin/templates/games.html
@@ -9,7 +9,7 @@
{{ range .Games }}
<li>
<a href="/admin/games/{{ .GameID }}">
- {{ .DisplayName }} (id={{ .GameID }})
+ {{ .DisplayName }} (id={{ .GameID }} type={{ .GameType }} state={{ .State }})
</a>
</li>
{{ end }}
diff --git a/backend/api/generated.go b/backend/api/generated.go
index ea1c315..e371c7d 100644
--- a/backend/api/generated.go
+++ b/backend/api/generated.go
@@ -22,6 +22,12 @@ import (
strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo"
)
+// Defines values for GameGameType.
+const (
+ Multiplayer GameGameType = "multiplayer"
+ N1V1 GameGameType = "1v1"
+)
+
// Defines values for GameState.
const (
Closed GameState = "closed"
@@ -50,14 +56,18 @@ type Error struct {
// Game defines model for Game.
type Game struct {
- DisplayName string `json:"display_name"`
- DurationSeconds int `json:"duration_seconds"`
- GameID int `json:"game_id"`
- Problem *Problem `json:"problem,omitempty"`
- StartedAt *int `json:"started_at,omitempty"`
- State GameState `json:"state"`
+ DisplayName string `json:"display_name"`
+ DurationSeconds int `json:"duration_seconds"`
+ GameID int `json:"game_id"`
+ GameType GameGameType `json:"game_type"`
+ Problem *Problem `json:"problem,omitempty"`
+ StartedAt *int `json:"started_at,omitempty"`
+ State GameState `json:"state"`
}
+// GameGameType defines model for Game.GameType.
+type GameGameType string
+
// GameState defines model for Game.State.
type GameState string
@@ -1091,31 +1101,31 @@ func (sh *strictHandler) GetToken(ctx echo.Context, params GetTokenParams) error
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
- "//9iv6ZqPCEAAA==",
+ "H4sIAAAAAAAC/9xZbW/bNhD+Kxo3oBug+S1B0flbmrVZh64z6hb7UAQGLZ1tZhKpklQSL9B/H0jqxbQo",
+ "S3aUrFg+BLbEu3vu7uHxfHxAAYsTRoFKgaYPKMEcxyCB628bwCHwBU7lhnHyD5aEUfWcUDTNXyIfURwD",
+ "mqILa5WPOHxNCYcQTSVPwUci2ECMlbjcJkpASE7oGmWZjxIsN4s1jmFBwtKAelipL952UEyohDVwlCnV",
+ "HETCqADt0GscfoSvKQipvgWMSqD6I06SiAQa+vBGGC8rvT9wWKEp+n5YBWto3orhG85ZbioEEXCSmCgp",
+ "Wx7PjWU+esv4koQh0Ke3XJnKfPSBybcspeHTm/3ApLfSpjIffaYFa+AZTFvW1OtcQik0QorbnCXAJTFU",
+ "iEEIvAb1Ee5xnESKOe/oLY5IlTffwdWKfl9KJdflQra8gUAn/Erzdt9sSEQS4e2C5m8r22q9N66b9FGY",
+ "ch2thYCA0VBYcmcvR36N+D7a2Uzl0nHjQvP4AQFNY+XX+FYBidNIEoUWuPKwgmpe13AmnC0jiNuyOMuX",
+ "qTRJzCWECywtoL+cv3z56vzVyOmZkFhaYIOICVCF4Q4TSeh6AVRyFe7qibaDFEJIMAeUW1a4dQTMhxWh",
+ "RGwgtJ0t1R+mQlWfqogWYH077Y6MNhFopqP/R8VVRuHPFZp+ORzimuh8coky/0ihy8kcZdcuJOrN6WAu",
+ "J/M3VPLtSYg+Ag5Pk7xkIZwkOE+XMZHNodCK6zsdy9aC1qRthrcRw7qQFlszYFSdWsjsx2kwEdNA2W3j",
+ "ZU5EjaYTy/Yg1PwKcm+rHZJwQuWPL36DKGK+d8d4FH734qdWZFpRV0iGMDUwB6IDWqJTeLqCMNw7BgTX",
+ "Er2CyNnYG9+Mvm6ME8b2U3DOhvEtsE7VzEdV3Fl+2hxdc+aTy7k+sU6RfHMPwUcQadRUsew1/fDI0tnO",
+ "JTEJpnAPATcYeueTE07NUxEwbpNqrFoOmkYRXqqv5veFuwVJxW4PItIgACHszqF42OZers7PAXX1sKBX",
+ "bxnMFXZLX9VK9Z+7PSA1B49tNPcgFeJd4Zi92FuYtbpuQS761v5DbIGo7wz19oiuvE5oI96E5i8sg82J",
+ "fa0tqxvba6fao+t3Tbx7Ea6Jdm42a5Ku+u1WfzIjneoOMPLOrNeU7K3pPAiix/PfzzdUh1/D+3WilPMP",
+ "tw2HcthfkjodsLup6vmE7QCoXqm7ht7/705jpSEEzm16NaxjqV0UkcW/1jjvUmrv2C/Vl3g6J+KRB5Rb",
+ "X0eS9XdEHYbxrGfUrGow9kK6O3bcpcGnDREeER72iu6ieUDWaTtIIqO9ipejcg0J3R2O4ZnRZI9MXU5/",
+ "FsCPGVj+zjbU+5WBy1MSMLrQA3xLZEhivAYxvGEbOrhJ1k5RscBhTOz4rnAkqs2/ZCwCrMfbqXCUl8mZ",
+ "K6Jqad0LBaU1noWVHSW1mV6Jux5bpY7QFdPDApNXdBEtseRMCE9B5BRH3h0svYvZO+SjW+DCTLZHg/Fg",
+ "pNCzBChOCJqis8FoMELm0kSnaLjGsUnWGvR2UPnT88V3IZqiK5BXeoFvXe80NETVkqHz+ie73rtTmYxG",
+ "Rw34bXqV0ImEWHSpVlVBQphzvHVOYkVDFuxrg/dESI+tPCOR+eh8NG6CUPo8tC8blNBZu9DOnYw6SNI4",
+ "xnxbQMjtZ36eyuFDPk3O2pLaU079Vjnrhu4JONAt845Md0r0hQ7xs2VYSZy3S5RXczYlrkB6OAesKBGx",
+ "tamGCRMOJsyYkO/1EhMcEPI1M2PKE/ORYCHuGA/3+u386Xhy5irbj6yutCBzbtqdVfvqN+uVhZL9DXuH",
+ "+r36G+z8b+9ztJIulJybdnSVRtHWU3QDKhXUgnFH09TikDrLPUMczaHSuaZi8kkv+BZPiP9VXszeFhvG",
+ "5c8RuYXQw9qcZwBmWZb9GwAA//9z5sw+kyEAAA==",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/backend/api/handlers.go b/backend/api/handlers.go
index a824f17..659e5c1 100644
--- a/backend/api/handlers.go
+++ b/backend/api/handlers.go
@@ -87,6 +87,7 @@ func (h *ApiHandler) GetGames(ctx context.Context, request GetGamesRequestObject
}
games[i] = Game{
GameID: int(row.GameID),
+ GameType: GameGameType(row.GameType),
State: GameState(row.State),
DisplayName: row.DisplayName,
DurationSeconds: int(row.DurationSeconds),
@@ -134,6 +135,7 @@ func (h *ApiHandler) GetGame(ctx context.Context, request GetGameRequestObject,
}
game := Game{
GameID: int(row.GameID),
+ GameType: GameGameType(row.GameType),
State: GameState(row.State),
DisplayName: row.DisplayName,
DurationSeconds: int(row.DurationSeconds),
diff --git a/backend/db/models.go b/backend/db/models.go
index 5ad4b6b..51157bc 100644
--- a/backend/db/models.go
+++ b/backend/db/models.go
@@ -10,6 +10,7 @@ import (
type Game struct {
GameID int32
+ GameType string
State string
DisplayName string
DurationSeconds int32
diff --git a/backend/db/query.sql.go b/backend/db/query.sql.go
index 074e767..8df3bf5 100644
--- a/backend/db/query.sql.go
+++ b/backend/db/query.sql.go
@@ -12,7 +12,7 @@ import (
)
const getGameByID = `-- name: GetGameByID :one
-SELECT game_id, state, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description FROM games
+SELECT game_id, game_type, state, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description FROM games
LEFT JOIN problems ON games.problem_id = problems.problem_id
WHERE games.game_id = $1
LIMIT 1
@@ -20,6 +20,7 @@ LIMIT 1
type GetGameByIDRow struct {
GameID int32
+ GameType string
State string
DisplayName string
DurationSeconds int32
@@ -36,6 +37,7 @@ func (q *Queries) GetGameByID(ctx context.Context, gameID int32) (GetGameByIDRow
var i GetGameByIDRow
err := row.Scan(
&i.GameID,
+ &i.GameType,
&i.State,
&i.DisplayName,
&i.DurationSeconds,
@@ -107,13 +109,60 @@ func (q *Queries) GetUserByID(ctx context.Context, userID int32) (User, error) {
return i, err
}
+const listGamePlayers = `-- name: ListGamePlayers :many
+SELECT game_id, game_players.user_id, users.user_id, username, display_name, icon_path, is_admin, created_at FROM game_players
+LEFT JOIN users ON game_players.user_id = users.user_id
+WHERE game_players.game_id = $1
+`
+
+type ListGamePlayersRow struct {
+ GameID int32
+ UserID int32
+ UserID_2 *int32
+ Username *string
+ DisplayName *string
+ IconPath *string
+ IsAdmin *bool
+ CreatedAt pgtype.Timestamp
+}
+
+func (q *Queries) ListGamePlayers(ctx context.Context, gameID int32) ([]ListGamePlayersRow, error) {
+ rows, err := q.db.Query(ctx, listGamePlayers, gameID)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []ListGamePlayersRow
+ for rows.Next() {
+ var i ListGamePlayersRow
+ if err := rows.Scan(
+ &i.GameID,
+ &i.UserID,
+ &i.UserID_2,
+ &i.Username,
+ &i.DisplayName,
+ &i.IconPath,
+ &i.IsAdmin,
+ &i.CreatedAt,
+ ); err != nil {
+ return nil, err
+ }
+ items = append(items, i)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
const listGames = `-- name: ListGames :many
-SELECT game_id, state, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description FROM games
+SELECT game_id, game_type, state, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description FROM games
LEFT JOIN problems ON games.problem_id = problems.problem_id
`
type ListGamesRow struct {
GameID int32
+ GameType string
State string
DisplayName string
DurationSeconds int32
@@ -136,6 +185,7 @@ func (q *Queries) ListGames(ctx context.Context) ([]ListGamesRow, error) {
var i ListGamesRow
if err := rows.Scan(
&i.GameID,
+ &i.GameType,
&i.State,
&i.DisplayName,
&i.DurationSeconds,
@@ -157,7 +207,7 @@ func (q *Queries) ListGames(ctx context.Context) ([]ListGamesRow, error) {
}
const listGamesForPlayer = `-- name: ListGamesForPlayer :many
-SELECT games.game_id, state, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description, game_players.game_id, user_id FROM games
+SELECT games.game_id, game_type, state, display_name, duration_seconds, created_at, started_at, games.problem_id, problems.problem_id, title, description, game_players.game_id, user_id FROM games
LEFT JOIN problems ON games.problem_id = problems.problem_id
JOIN game_players ON games.game_id = game_players.game_id
WHERE game_players.user_id = $1
@@ -165,6 +215,7 @@ WHERE game_players.user_id = $1
type ListGamesForPlayerRow struct {
GameID int32
+ GameType string
State string
DisplayName string
DurationSeconds int32
@@ -189,6 +240,7 @@ func (q *Queries) ListGamesForPlayer(ctx context.Context, userID int32) ([]ListG
var i ListGamesForPlayerRow
if err := rows.Scan(
&i.GameID,
+ &i.GameType,
&i.State,
&i.DisplayName,
&i.DurationSeconds,
@@ -245,16 +297,18 @@ func (q *Queries) ListUsers(ctx context.Context) ([]User, error) {
const updateGame = `-- name: UpdateGame :exec
UPDATE games
SET
- state = $2,
- display_name = $3,
- duration_seconds = $4,
- started_at = $5,
- problem_id = $6
+ game_type = $2,
+ state = $3,
+ display_name = $4,
+ duration_seconds = $5,
+ started_at = $6,
+ problem_id = $7
WHERE game_id = $1
`
type UpdateGameParams struct {
GameID int32
+ GameType string
State string
DisplayName string
DurationSeconds int32
@@ -265,6 +319,7 @@ type UpdateGameParams struct {
func (q *Queries) UpdateGame(ctx context.Context, arg UpdateGameParams) error {
_, err := q.db.Exec(ctx, updateGame,
arg.GameID,
+ arg.GameType,
arg.State,
arg.DisplayName,
arg.DurationSeconds,
diff --git a/backend/fixtures/dev.sql b/backend/fixtures/dev.sql
index 5e47386..a468d01 100644
--- a/backend/fixtures/dev.sql
+++ b/backend/fixtures/dev.sql
@@ -17,14 +17,20 @@ INSERT INTO problems
VALUES
('TEST problem 1', 'This is TEST problem 1'),
('TEST problem 2', 'This is TEST problem 2'),
- ('TEST problem 3', 'This is TEST problem 3');
+ ('TEST problem 3', 'This is TEST problem 3'),
+ ('TEST problem 4', 'This is TEST problem 4'),
+ ('TEST problem 5', 'This is TEST problem 5'),
+ ('TEST problem 6', 'This is TEST problem 6');
INSERT INTO games
-(state, display_name, duration_seconds, problem_id)
+(game_type, state, display_name, duration_seconds, problem_id)
VALUES
- ('waiting_entries', 'TEST game 1', 180, 1),
- ('closed', 'TEST game 2', 180, 2),
- ('finished', 'TEST game 3', 180, 3);
+ ('1v1', 'waiting_entries', 'TEST game 1', 180, 1),
+ ('1v1', 'closed', 'TEST game 2', 180, 2),
+ ('1v1', 'finished', 'TEST game 3', 180, 3),
+ ('multiplayer', 'waiting_start', 'TEST game 4', 180, 4),
+ ('multiplayer', 'closed', 'TEST game 5', 180, 5),
+ ('multiplayer', 'finished', 'TEST game 6', 180, 6);
INSERT INTO game_players
(game_id, user_id)
@@ -34,4 +40,10 @@ VALUES
(2, 1),
(2, 2),
(3, 1),
- (3, 2);
+ (3, 2),
+ (4, 1),
+ (4, 2),
+ (5, 1),
+ (5, 2),
+ (6, 1),
+ (6, 2);
diff --git a/backend/game/http.go b/backend/game/http.go
index f9036c5..865c724 100644
--- a/backend/game/http.go
+++ b/backend/game/http.go
@@ -56,5 +56,10 @@ func (h *sockHandler) HandleSockGolfWatch(c echo.Context) error {
if hub == nil {
return echo.NewHTTPError(http.StatusNotFound, "Game not found")
}
+
+ if hub.game.gameType != gameType1v1 {
+ return echo.NewHTTPError(http.StatusBadRequest, "Only 1v1 game is supported")
+ }
+
return serveWatcherWs(hub, c.Response(), c.Request())
}
diff --git a/backend/game/hub.go b/backend/game/hub.go
index 239f5da..70bf71f 100644
--- a/backend/game/hub.go
+++ b/backend/game/hub.go
@@ -81,7 +81,7 @@ func (hub *gameHub) run() {
entriedPlayerCount++
}
}
- if entriedPlayerCount == 2 {
+ if entriedPlayerCount == hub.game.playerCount {
err := hub.q.UpdateGameState(hub.ctx, db.UpdateGameStateParams{
GameID: int32(hub.game.gameID),
State: string(gameStateWaitingStart),
@@ -101,7 +101,7 @@ func (hub *gameHub) run() {
readyPlayerCount++
}
}
- if readyPlayerCount == 2 {
+ if readyPlayerCount == hub.game.playerCount {
startAt := time.Now().Add(11 * time.Second).UTC()
for player := range hub.players {
player.s2cMessages <- &playerMessageS2CStart{
@@ -300,6 +300,11 @@ func (hubs *GameHubs) RestoreFromDB(ctx context.Context) error {
description: *row.Description,
}
}
+ // TODO: N+1
+ playerRows, err := hubs.q.ListGamePlayers(ctx, int32(row.GameID))
+ if err != nil {
+ return err
+ }
hubs.hubs[int(row.GameID)] = newGameHub(ctx, &game{
gameID: int(row.GameID),
durationSeconds: int(row.DurationSeconds),
@@ -307,6 +312,7 @@ func (hubs *GameHubs) RestoreFromDB(ctx context.Context) error {
displayName: row.DisplayName,
startedAt: startedAt,
problem: problem_,
+ playerCount: len(playerRows),
}, hubs.q)
}
return nil
diff --git a/backend/game/models.go b/backend/game/models.go
index 6c299d6..4080482 100644
--- a/backend/game/models.go
+++ b/backend/game/models.go
@@ -6,9 +6,13 @@ import (
"github.com/nsfisis/iosdc-japan-2024-albatross/backend/api"
)
+type gameType = api.GameGameType
type gameState = api.GameState
const (
+ gameType1v1 = api.N1V1
+ gameTypeMultiplayer = api.Multiplayer
+
gameStateClosed gameState = api.Closed
gameStateWaitingEntries gameState = api.WaitingEntries
gameStateWaitingStart gameState = api.WaitingStart
@@ -20,11 +24,13 @@ const (
type game struct {
gameID int
+ gameType gameType
state gameState
displayName string
durationSeconds int
startedAt *time.Time
problem *problem
+ playerCount int
}
type problem struct {
diff --git a/backend/query.sql b/backend/query.sql
index 886f96c..245d5cf 100644
--- a/backend/query.sql
+++ b/backend/query.sql
@@ -38,12 +38,18 @@ LEFT JOIN problems ON games.problem_id = problems.problem_id
WHERE games.game_id = $1
LIMIT 1;
+-- name: ListGamePlayers :many
+SELECT * FROM game_players
+LEFT JOIN users ON game_players.user_id = users.user_id
+WHERE game_players.game_id = $1;
+
-- name: UpdateGame :exec
UPDATE games
SET
- state = $2,
- display_name = $3,
- duration_seconds = $4,
- started_at = $5,
- problem_id = $6
+ game_type = $2,
+ state = $3,
+ display_name = $4,
+ duration_seconds = $5,
+ started_at = $6,
+ problem_id = $7
WHERE game_id = $1;
diff --git a/backend/schema.sql b/backend/schema.sql
index 4c63511..4a1d4b0 100644
--- a/backend/schema.sql
+++ b/backend/schema.sql
@@ -25,6 +25,7 @@ CREATE TABLE problems (
CREATE TABLE games (
game_id SERIAL PRIMARY KEY,
+ game_type VARCHAR(16) NOT NULL,
state VARCHAR(32) NOT NULL,
display_name VARCHAR(255) NOT NULL,
duration_seconds INT NOT NULL,
diff --git a/frontend/app/.server/api/schema.d.ts b/frontend/app/.server/api/schema.d.ts
index 88067a8..62badcf 100644
--- a/frontend/app/.server/api/schema.d.ts
+++ b/frontend/app/.server/api/schema.d.ts
@@ -96,6 +96,11 @@ export interface components {
/** @example 1 */
game_id: number;
/**
+ * @example 1v1
+ * @enum {string}
+ */
+ game_type: "1v1" | "multiplayer";
+ /**
* @example closed
* @enum {string}
*/
diff --git a/frontend/app/routes/dashboard.tsx b/frontend/app/routes/dashboard.tsx
index e23d7aa..e6a43de 100644
--- a/frontend/app/routes/dashboard.tsx
+++ b/frontend/app/routes/dashboard.tsx
@@ -42,7 +42,8 @@ export default function Dashboard() {
<ul className="list-disc list-inside">
{games.map((game) => (
<li key={game.game_id}>
- {game.display_name}{" "}
+ {game.display_name}
+ {game.game_type === "multiplayer" ? " (Multiplayer)" : " (1v1)"}
{game.state === "closed" || game.state === "finished" ? (
<span className="inline-block px-6 py-2 text-gray-400 bg-gray-200 cursor-not-allowed rounded">
Entry
diff --git a/frontend/app/routes/golf.$gameId.watch.tsx b/frontend/app/routes/golf.$gameId.watch.tsx
index 83b7a1a..5edf92f 100644
--- a/frontend/app/routes/golf.$gameId.watch.tsx
+++ b/frontend/app/routes/golf.$gameId.watch.tsx
@@ -25,6 +25,11 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
};
const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]);
+
+ if (game.game_type !== "1v1") {
+ return new Response("Not Found", { status: 404 });
+ }
+
return {
game,
sockToken,
diff --git a/openapi.yaml b/openapi.yaml
index 2c91ad1..2e18728 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -185,6 +185,12 @@ components:
game_id:
type: integer
example: 1
+ game_type:
+ type: string
+ example: "1v1"
+ enum:
+ - 1v1
+ - multiplayer
state:
type: string
example: "closed"
@@ -209,6 +215,7 @@ components:
$ref: '#/components/schemas/Problem'
required:
- game_id
+ - game_type
- state
- display_name
- duration_seconds