diff --git a/internal/infra/ws/servewebsockets.go b/internal/infra/ws/servewebsockets.go index 61a3064..1431742 100644 --- a/internal/infra/ws/servewebsockets.go +++ b/internal/infra/ws/servewebsockets.go @@ -46,7 +46,7 @@ func (w WSHandler) websocketHandler(user auth.User, res http.ResponseWriter, req return } - userSocket := messages.NewUserSocket(newSocketClient(conn), user) + userSocket := messages.NewUserClient(newSocketClient(conn), user) err = room.Hub().Register(userSocket) if err != nil { http.Error(res, feelbeaterror.RoomNotFound, feelbeaterror.StatusCode(feelbeaterror.RoomNotFound)) diff --git a/internal/infra/ws/wshub.go b/internal/infra/ws/wshub.go index 057aa85..78816e7 100644 --- a/internal/infra/ws/wshub.go +++ b/internal/infra/ws/wshub.go @@ -144,6 +144,7 @@ func (h *WSHub) passMessages(ctx context.Context, wg *sync.WaitGroup, from strin func decodeMessage(from string, msgType string, payload []byte) messages.ClientMessage { var settingsUpdate messages.SettingsUpdatePayload + var guess messages.GuessSongPayload var err error var result interface{} @@ -153,6 +154,9 @@ func decodeMessage(from string, msgType string, payload []byte) messages.ClientM result = settingsUpdate case messages.ReadyStatus: result, err = jsonparser.GetBoolean(payload) + case messages.GuessSong: + err = json.Unmarshal(payload, &guess) + result = guess } if err != nil { diff --git a/internal/lib/audioprovider/ytaudioprovider.go b/internal/lib/audioprovider/ytaudioprovider.go index 4a9277d..50ddee1 100644 --- a/internal/lib/audioprovider/ytaudioprovider.go +++ b/internal/lib/audioprovider/ytaudioprovider.go @@ -67,13 +67,12 @@ func (y YTAudioProvider) pickYoutubeVideo(song lib.SongDetails) (string, error) // return deltas[i].delta < deltas[j].delta // }) - for _, d := range deltas { - fmt.Println(d.result.Title) - fmt.Println(d.result.Duration) - fmt.Println(d.delta) - fmt.Println(song.Duration) - fmt.Println("----") - } + d := deltas[0] + fmt.Println(d.result.Title) + fmt.Println(d.result.Duration) + fmt.Println(d.delta) + fmt.Println(song.Duration) + fmt.Println("----") return deltas[0].result.VideoId, nil } diff --git a/internal/lib/messages/client.go b/internal/lib/messages/client.go index 9e3c157..3a2a31b 100644 --- a/internal/lib/messages/client.go +++ b/internal/lib/messages/client.go @@ -9,6 +9,7 @@ const ( LeavingPlayer = "LEAVE" SettingsUpdate = "SETTINGS_UPDATE" ReadyStatus = "READY_STATUS" + GuessSong = "GUESS_SONG" ) type ClientMessage struct { @@ -25,3 +26,8 @@ type SettingsUpdatePayload struct { Token string `json:"token"` Settings lib.RoomSettings `json:"settings"` } + +type GuessSongPayload struct { + Id string `json:"id"` + Points int `json:"points"` +} diff --git a/internal/lib/messages/server.go b/internal/lib/messages/server.go index 3cc491f..b9f20b8 100644 --- a/internal/lib/messages/server.go +++ b/internal/lib/messages/server.go @@ -32,6 +32,9 @@ const ( RoomStage = "ROOM_STAGE" PlayerReady = "PLAYER_READY" PlaySong = "PLAY_SONG" + PlayerGuess = "PLAYER_GUESS" + CorrectSong = "CORRECT_SONG" + EndGame = "END_GAME" ) type InitialGameState struct { @@ -73,3 +76,19 @@ type PlaySongPayload struct { Timestamp int64 `json:"timestamp"` Duration int64 `json:"duration"` } + +type PlayerGuessPayload struct { + Correct bool `json:"correct"` + Points int `json:"points"` + PlayerId string `json:"playerId"` + SongId string `json:"songId"` +} + +type EndGamePayload struct { + Results []PlayerResult `json:"results"` +} + +type PlayerResult struct { + Profile lib.UserProfile `json:"profile"` + Points int `json:"points"` +} diff --git a/internal/lib/messages/usersocket.go b/internal/lib/messages/userclient.go similarity index 77% rename from internal/lib/messages/usersocket.go rename to internal/lib/messages/userclient.go index 2340b5b..6cccc42 100644 --- a/internal/lib/messages/usersocket.go +++ b/internal/lib/messages/userclient.go @@ -9,7 +9,7 @@ type UserClient struct { User auth.User } -func NewUserSocket(client HubClient, user auth.User) UserClient { +func NewUserClient(client HubClient, user auth.User) UserClient { return UserClient{ Client: client, User: user, diff --git a/internal/lib/room/processMessages.go b/internal/lib/room/processMessages.go index 3c7f5f0..396e5b4 100644 --- a/internal/lib/room/processMessages.go +++ b/internal/lib/room/processMessages.go @@ -31,6 +31,12 @@ func (r *Room) processMessages() { } else { logIncorrectPayload("Incorrect paylod in ready status", message.Payload, message.From) } + case messages.GuessSong: + if payload, ok := message.Payload.(messages.GuessSongPayload); ok { + r.verifyGuess(message.From, payload.Id, payload.Points) + } else { + logIncorrectPayload("Incorrect paylod in ready status", message.Payload, message.From) + } default: fblog.Warn(component.Room, "Received unexpected message", "room", r.id, "from", message.From, "type", message.Type, "payload", message.Payload) } diff --git a/internal/lib/room/room.go b/internal/lib/room/room.go index c4b6040..6b034e3 100644 --- a/internal/lib/room/room.go +++ b/internal/lib/room/room.go @@ -20,7 +20,7 @@ type Room struct { playlist lib.PlaylistData owner lib.UserProfile settings lib.RoomSettings - players map[string]Player + players map[string]*Player readyMap map[string]bool stage RoomStage hub messages.Hub @@ -29,10 +29,18 @@ type Room struct { onCleanup func(*Room) spotifyApi lib.SpotifyApi audio audioprovider.AudioProvider + game gameState } type Player struct { profile lib.UserProfile + points int + guessed bool +} + +type gameState struct { + turn int + pickedSong *lib.Song } func NewRoom(id string, @@ -49,7 +57,7 @@ func NewRoom(id string, playlist: playlist, owner: owner, settings: settings, - players: make(map[string]Player), + players: make(map[string]*Player), readyMap: make(map[string]bool), stage: LobbyStage, hub: hub, @@ -57,6 +65,10 @@ func NewRoom(id string, spotifyApi: spotifyApi, audio: audio, onCleanup: onCleanup, + game: gameState{ + turn: 0, + pickedSong: nil, + }, } } @@ -103,8 +115,10 @@ func (r *Room) addPlayer(profile lib.UserProfile) { return } - r.players[profile.Id] = Player{ + r.players[profile.Id] = &Player{ profile: profile, + points: 0, + guessed: false, } fblog.Info(component.Room, "new player", "roomId", r.id, "userId", profile.Id) @@ -186,6 +200,34 @@ func (r *Room) removePlayer(id string) { r.broadcastRoomStage(GameStage) r.provideSong() } + + if r.stage == GameStage && r.guessCount() == len(r.players) { + r.finishTurn() + if r.game.turn == r.settings.TurnCount { + r.endGame() + return + } + r.provideSong() + } + +} + +func (r *Room) guessCount() int { + c := 0 + for _, p := range r.players { + if p.guessed { + c++ + } + } + + return c +} + +func (r *Room) finishTurn() { + r.game.turn++ + for _, p := range r.players { + p.guessed = false + } } func (r *Room) updateSettings(from string, settingsPayload messages.SettingsUpdatePayload) { @@ -264,11 +306,10 @@ func (r *Room) broadcastRoomStage(stage RoomStage) { } func (r *Room) provideSong() { - var pickedSong lib.Song var url string for { - pickedSong = r.playlist.Songs[rand.IntN(len(r.playlist.Songs))] - u, err := r.audio.GetUrl(pickedSong.Details) + r.game.pickedSong = &r.playlist.Songs[rand.IntN(len(r.playlist.Songs))] + u, err := r.audio.GetUrl(r.game.pickedSong.Details) url = u if err == nil { break @@ -288,11 +329,102 @@ func (r *Room) provideSong() { Payload: messages.PlaySongPayload{ Url: url, Timestamp: start.Unix(), - Duration: pickedSong.Details.Duration.Milliseconds(), + Duration: r.game.pickedSong.Details.Duration.Milliseconds(), }, } } +func (r *Room) verifyGuess(from string, guess string, points int) { + if r.stage != GameStage || r.game.pickedSong == nil { + return + } + + var correct bool + var receivedPoints int + + if points == 0 { + correct = false + receivedPoints = 0 + } else { + if r.game.pickedSong.Id == guess { + r.players[from].points += points + receivedPoints = points + correct = true + } else { + r.players[from].points -= r.settings.IncorrectGuessPenalty + receivedPoints = -r.settings.IncorrectGuessPenalty + correct = false + } + } + + r.sendToAllExcept(from, messages.PlayerGuess, messages.PlayerGuessPayload{ + Correct: correct, + Points: receivedPoints, + PlayerId: from, + SongId: "", + }) + + if points == 0 { + r.snd <- messages.ServerMessage{ + Type: messages.CorrectSong, + To: []string{from}, + Payload: r.game.pickedSong.Id, + } + } else { + r.snd <- messages.ServerMessage{ + Type: messages.PlayerGuess, + To: []string{from}, + Payload: messages.PlayerGuessPayload{ + Correct: correct, + Points: receivedPoints, + PlayerId: from, + SongId: guess, + }, + } + } + + if points == 0 || correct { + r.players[from].guessed = true + + if r.guessCount() == len(r.players) { + r.finishTurn() + + if r.game.turn == r.settings.TurnCount { + r.endGame() + return + } + + r.provideSong() + } + } +} + +func (r *Room) endGame() { + recipents := make([]string, 0) + results := make([]messages.PlayerResult, 0) + for _, p := range r.players { + recipents = append(recipents, p.profile.Id) + results = append(results, messages.PlayerResult{ + Profile: p.profile, + Points: p.points, + }) + r.readyMap[p.profile.Id] = false + } + + time.Sleep(time.Second * 3) + r.snd <- messages.ServerMessage{ + Type: messages.EndGame, + To: recipents, + Payload: messages.EndGamePayload{ + Results: results, + }, + } + + r.stage = LobbyStage + r.game.turn = 0 + r.game.pickedSong = nil +} + func (r *Room) sendToAllExcept(id string, messageType messages.ServerMessageType, payload interface{}) { recipents := make([]string, 0) for _, p := range r.players {