From b66f9c4c0675147ecbc9dd073c92ce7ae7b72655 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Wed, 2 Jul 2025 11:18:28 +0300 Subject: [PATCH] Enha: model update --- handlers/actions.go | 81 ++++++++++++++-------------- handlers/handlers.go | 28 ++++++---- migrations/001_initial_schema.up.sql | 2 +- models/main.go | 12 +++-- repos/players.go | 18 ++++++- repos/rooms.go | 72 ++++++++++++++++++++++++- 6 files changed, 153 insertions(+), 60 deletions(-) diff --git a/handlers/actions.go b/handlers/actions.go index 4e34066..ff45895 100644 --- a/handlers/actions.go +++ b/handlers/actions.go @@ -20,42 +20,43 @@ func createRoom(ctx context.Context, req *models.RoomReq) (*models.Room, error) } room := req.CreateRoom(creator) room.RoomLink = cfg.BaseURL + "/room-join?id=" + room.ID - if err := saveRoom(room); err != nil { + if err := repo.CreateRoom(ctx, room); err != nil { return nil, err } return room, nil } -func saveRoom(room *models.Room) error { - key := models.CacheRoomPrefix + room.ID - data, err := json.Marshal(room) - if err != nil { - return err - } - memcache.Set(key, data) - // do I need last action here? since room save is kind of an action on itself - // time.Now().Add(time.Hour).Sub(room.LastActionTS) - anHour := int64(216000) // 60 * 60 * 60 - memcache.Expire(key, anHour) - return nil -} +// // DEPRECATED +// func saveRoom(room *models.Room) error { +// key := models.CacheRoomPrefix + room.ID +// data, err := json.Marshal(room) +// if err != nil { +// return err +// } +// memcache.Set(key, data) +// // do I need last action here? since room save is kind of an action on itself +// // time.Now().Add(time.Hour).Sub(room.LastActionTS) +// anHour := int64(216000) // 60 * 60 * 60 +// memcache.Expire(key, anHour) +// return nil +// } -func getRoomByID(roomID string) (*models.Room, error) { - roomBytes, err := memcache.Get(models.CacheRoomPrefix + roomID) - if err != nil { - return nil, err - } - resp := &models.Room{} - if err := json.Unmarshal(roomBytes, &resp); err != nil { - return nil, err - } - return resp, nil -} +// func getRoomByID(roomID string) (*models.Room, error) { +// roomBytes, err := memcache.Get(models.CacheRoomPrefix + roomID) +// if err != nil { +// return nil, err +// } +// resp := &models.Room{} +// if err := json.Unmarshal(roomBytes, &resp); err != nil { +// return nil, err +// } +// return resp, nil +// } -func removeRoom(roomID string) { - key := models.CacheRoomPrefix + roomID - memcache.RemoveKey(key) -} +// func removeRoom(roomID string) { +// key := models.CacheRoomPrefix + roomID +// memcache.RemoveKey(key) +// } // context @@ -76,17 +77,17 @@ func getStateByCtx(ctx context.Context) (*models.UserState, error) { // repo.CreateRoom() // } -func saveFullInfo(fi *models.FullInfo) error { - // INFO: no transactions; so case is possible where first object is updated but the second is not - if err := saveState(fi.State.Username, fi.State); err != nil { - return err - } - log.Debug("saved user state", "state", fi.State) - if err := saveRoom(fi.Room); err != nil { - return err - } - return nil -} +// func saveFullInfo(fi *models.FullInfo) error { +// // INFO: no transactions; so case is possible where first object is updated but the second is not +// if err := saveState(fi.State.Username, fi.State); err != nil { +// return err +// } +// log.Debug("saved user state", "state", fi.State) +// if err := saveRoom(fi.Room); err != nil { +// return err +// } +// return nil +// } func notifyBotIfNeeded(room *models.Room) { if botName := room.WhichBotToMove(); botName != "" { diff --git a/handlers/handlers.go b/handlers/handlers.go index 4a0c22e..b5d3622 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -14,9 +14,9 @@ import ( ) var ( - log *slog.Logger - cfg *config.Config - memcache cache.Cache + log *slog.Logger + cfg *config.Config + // memcache cache.Cache Notifier *broker.Broker repo repos.AllRepos ) @@ -26,7 +26,7 @@ func init() { Level: slog.LevelDebug, AddSource: true, })) - memcache = cache.MemCache + // memcache = cache.MemCache cfg = config.LoadConfigOrDefault("") Notifier = broker.Notifier cache.MemCache.StartBackupRoutine(15 * time.Second) // Reduced backup interval @@ -91,17 +91,25 @@ func HandleExit(w http.ResponseWriter, r *http.Request) { creatorLeft = true } exitedRoom := fi.ExitRoom() - if err := saveRoom(exitedRoom); err != nil { - abortWithError(w, err.Error()) - return - } + // if err := saveRoom(exitedRoom); err != nil { + // abortWithError(w, err.Error()) + // return + // } if creatorLeft { - removeRoom(exitedRoom.ID) + if err := repo.DeleteRoomByID(r.Context(), exitedRoom.ID); err != nil { + log.Error("failed to remove room", "error", err) + } + // removeRoom(exitedRoom.ID) // TODO: notify users if creator left // and throw them away notify(models.NotifyRoomListUpdate, "") } - if err := saveState(fi.State.Username, fi.State); err != nil { + // scary to update the whole room + if err := repo.UpdateRoom(r.Context(), exitedRoom); err != nil { + abortWithError(w, err.Error()) + return + } + if err := repo.PlayerExitRoom(fi.State.Username); err != nil { abortWithError(w, err.Error()) return } diff --git a/migrations/001_initial_schema.up.sql b/migrations/001_initial_schema.up.sql index 1e4acfe..076fd14 100644 --- a/migrations/001_initial_schema.up.sql +++ b/migrations/001_initial_schema.up.sql @@ -14,7 +14,7 @@ CREATE TABLE rooms ( is_running BOOLEAN NOT NULL DEFAULT FALSE, is_over BOOLEAN NOT NULL DEFAULT FALSE, team_won TEXT NOT NULL DEFAULT '', - room_pass TEXT NOT NULL DEFAULT '' + room_link TEXT NOT NULL DEFAULT '' ); CREATE TABLE players ( diff --git a/models/main.go b/models/main.go index 6b5a307..b1c1ef7 100644 --- a/models/main.go +++ b/models/main.go @@ -373,11 +373,13 @@ func (r *Room) RevealSpecificWord(word string) { } type WordCard struct { - Word string `json:"word"` - Color WordColor `json:"color"` - Revealed bool `json:"revealed"` - Mime bool `json:"mime"` // user who sees that card is mime - Mark []CardMark `json:"marks"` + ID uint32 `json:"id" db:"id"` + RoomID string `json:"room_id" db:"room_id"` + Word string `json:"word" db:"word"` + Color WordColor `json:"color" db:"color"` + Revealed bool `json:"revealed" db:"revealed"` + MimeView bool `json:"mime_view" db:"mime_view"` // user who sees that card is mime + Mark []CardMark `json:"marks" db:"-"` } // table: settings diff --git a/repos/players.go b/repos/players.go index a90db5e..487e42c 100644 --- a/repos/players.go +++ b/repos/players.go @@ -10,6 +10,8 @@ type PlayersRepo interface { PlayerAdd(player *models.Player) error PlayerUpdate(player *models.Player) error PlayerDelete(roomID, username string) error + PlayerSetRoomID(username, roomID string) error + PlayerExitRoom(username string) error } func (p *RepoProvider) PlayerGetByName(username string) (*models.Player, error) { @@ -22,12 +24,14 @@ func (p *RepoProvider) PlayerGetByName(username string) (*models.Player, error) } func (p *RepoProvider) PlayerAdd(player *models.Player) error { - _, err := p.DB.ExecContext(context.Background(), "INSERT INTO players (room_id, username, team, role, is_bot) VALUES (?, ?, ?, ?, ?)", player.RoomID, player.Username, player.Team, player.Role, player.IsBot) + _, err := p.DB.ExecContext(context.Background(), "INSERT INTO players (room_id, username, team, role, is_bot) VALUES (?, ?, ?, ?, ?)", + player.RoomID, player.Username, player.Team, player.Role, player.IsBot) return err } func (p *RepoProvider) PlayerUpdate(player *models.Player) error { - _, err := p.DB.ExecContext(context.Background(), "UPDATE players SET room_id = ?, username = ?, team = ?, role = ?, is_bot = ? WHERE id = ?", player.RoomID, player.Username, player.Team, player.Role, player.IsBot, player.ID) + _, err := p.DB.ExecContext(context.Background(), "UPDATE players SET room_id = ?, username = ?, team = ?, role = ?, is_bot = ? WHERE id = ?", + player.RoomID, player.Username, player.Team, player.Role, player.IsBot, player.ID) return err } @@ -35,3 +39,13 @@ func (p *RepoProvider) PlayerDelete(roomID, username string) error { _, err := p.DB.ExecContext(context.Background(), "DELETE FROM players WHERE room_id = ? AND username = ?", roomID, username) return err } + +func (p *RepoProvider) PlayerSetRoomID(username, roomID string) error { + _, err := p.DB.ExecContext(context.Background(), "UPDATE players SET room_id = ? WHERE username = ?", roomID, username) + return err +} + +func (p *RepoProvider) PlayerExitRoom(username string) error { + _, err := p.DB.ExecContext(context.Background(), "UPDATE players SET room_id = null WHERE username = ?", username) + return err +} diff --git a/repos/rooms.go b/repos/rooms.go index 00ee133..799afd2 100644 --- a/repos/rooms.go +++ b/repos/rooms.go @@ -8,6 +8,7 @@ import ( type RoomsRepo interface { ListRooms(ctx context.Context) ([]*models.Room, error) GetRoomByID(ctx context.Context, id string) (*models.Room, error) + GetRoomExtended(ctx context.Context, id string) (*models.Room, error) CreateRoom(ctx context.Context, room *models.Room) error DeleteRoomByID(ctx context.Context, id string) error UpdateRoom(ctx context.Context, room *models.Room) error @@ -32,7 +33,7 @@ func (p *RepoProvider) GetRoomByID(ctx context.Context, id string) (*models.Room } func (p *RepoProvider) CreateRoom(ctx context.Context, r *models.Room) error { - _, err := p.DB.ExecContext(ctx, `INSERT INTO rooms (id, created_at, creator_name, team_turn, this_turn_limit, opened_this_turn, blue_counter, red_counter, red_turn, mime_done, is_running, round_time, is_over, team_won, room_pass) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, r.ID, r.CreatedAt, r.CreatorName, r.TeamTurn, r.ThisTurnLimit, r.OpenedThisTurn, r.BlueCounter, r.RedCounter, r.RedTurn, r.MimeDone, r.IsRunning, r.RoundTime, r.IsOver, r.TeamWon, r.RoomPass) + _, err := p.DB.ExecContext(ctx, `INSERT INTO rooms (id, created_at, creator_name, team_turn, this_turn_limit, opened_this_turn, blue_counter, red_counter, red_turn, mime_done, , is_running, is_over, team_won, room_link) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, r.ID, r.CreatedAt, r.CreatorName, r.TeamTurn, r.ThisTurnLimit, r.OpenedThisTurn, r.BlueCounter, r.RedCounter, r.RedTurn, r.MimeDone, r.IsRunning, r.IsOver, r.TeamWon, r.RoomLink) return err } @@ -42,6 +43,73 @@ func (p *RepoProvider) DeleteRoomByID(ctx context.Context, id string) error { } func (p *RepoProvider) UpdateRoom(ctx context.Context, r *models.Room) error { - _, err := p.DB.ExecContext(ctx, `UPDATE rooms SET team_turn = ?, this_turn_limit = ?, opened_this_turn = ?, blue_counter = ?, red_counter = ?, red_turn = ?, mime_done = ?, is_running = ?, round_time = ?, is_over = ?, team_won = ?, room_pass = ? WHERE id = ?`, r.TeamTurn, r.ThisTurnLimit, r.OpenedThisTurn, r.BlueCounter, r.RedCounter, r.RedTurn, r.MimeDone, r.IsRunning, r.RoundTime, r.IsOver, r.TeamWon, r.RoomPass, r.ID) + _, err := p.DB.ExecContext(ctx, `UPDATE rooms SET team_turn = ?, this_turn_limit = ?, opened_this_turn = ?, blue_counter = ?, red_counter = ?, red_turn = ?, mime_done = ?, = ?, is_running = ?, is_over = ?, team_won = ?, room_link = ? WHERE id = ?`, r.TeamTurn, r.ThisTurnLimit, r.OpenedThisTurn, r.BlueCounter, r.RedCounter, r.RedTurn, r.MimeDone, r.IsRunning, r.IsOver, r.TeamWon, r.RoomLink, r.ID) return err } + +func (p *RepoProvider) GetRoomExtended(ctx context.Context, id string) (*models.Room, error) { + room := &models.Room{} + err := p.DB.GetContext(ctx, room, `SELECT * FROM rooms WHERE id = ?`, id) + if err != nil { + return nil, err + } + + // Get players + players := []*models.Player{} + err = p.DB.SelectContext(ctx, &players, `SELECT * FROM players WHERE room_id = ?`, id) + if err != nil { + return nil, err + } + room.RedTeam.Color = string(models.UserTeamRed) + room.BlueTeam.Color = string(models.UserTeamBlue) + for _, player := range players { + if player.Team == models.UserTeamRed { + if player.Role == models.UserRoleMime { + room.RedTeam.Mime = player.Username + } else { + room.RedTeam.Guessers = append(room.RedTeam.Guessers, player.Username) + } + } else if player.Team == models.UserTeamBlue { + if player.Role == models.UserRoleMime { + room.BlueTeam.Mime = player.Username + } else { + room.BlueTeam.Guessers = append(room.BlueTeam.Guessers, player.Username) + } + } + if player.IsBot { + if room.BotMap == nil { + room.BotMap = make(map[string]models.BotPlayer) + } + room.BotMap[player.Username] = models.BotPlayer{ + Role: player.Role, + Team: player.Team, + } + } + } + + // Get word cards + wordCards := []*models.WordCard{} + err = p.DB.SelectContext(ctx, &wordCards, `SELECT * FROM word_cards WHERE room_id = ?`, id) + if err != nil { + return nil, err + } + room.Cards = wordCards + + // Get actions + actions := []*models.Action{} + err = p.DB.SelectContext(ctx, &actions, `SELECT * FROM actions WHERE room_id = ? ORDER BY created_at ASC`, id) + if err != nil { + return nil, err + } + room.ActionHistory = actions + + // Get settings + settings := &models.GameSettings{} + err = p.DB.GetContext(ctx, settings, `SELECT * FROM settings WHERE room_id = ?`, id) + if err != nil { + return nil, err + } + room.Settings = *settings + + return room, nil +}