diff --git a/internal/api/v1.go b/internal/api/v1.go index a0c456e8..de124545 100644 --- a/internal/api/v1.go +++ b/internal/api/v1.go @@ -88,6 +88,22 @@ func (handler *V1Handler) postLobby(writer http.ResponseWriter, request *http.Re return } + var requestErrors []string + + // The lobby ID is normally autogenerated and it isn't advisble to set it + // yourself, but for certain integrations / automations this can be useful. + // However, to avoid any kind of abuse, this needs to be a valid UUID at + // least. + desiredLobbyId := uuid.Nil + lobbyId := request.Form.Get("lobby_id") + if lobbyId != "" { + var err error + desiredLobbyId, err = uuid.FromString(lobbyId) + if err != nil { + requestErrors = append(requestErrors, err.Error()) + } + } + languageData, languageKey, languageInvalid := ParseLanguage(request.Form.Get("language")) drawingTime, drawingTimeInvalid := ParseDrawingTime(request.Form.Get("drawing_time")) rounds, roundsInvalid := ParseRounds(request.Form.Get("rounds")) @@ -104,7 +120,6 @@ func (handler *V1Handler) postLobby(writer http.ResponseWriter, request *http.Re } customWords, customWordsInvalid := ParseCustomWords(lowercaser, request.Form.Get("custom_words")) - var requestErrors []string if languageInvalid != nil { requestErrors = append(requestErrors, languageInvalid.Error()) } @@ -136,14 +151,23 @@ func (handler *V1Handler) postLobby(writer http.ResponseWriter, request *http.Re } playerName := GetPlayername(request) - player, lobby, err := game.CreateLobby(playerName, languageKey, - publicLobby, drawingTime, rounds, maxPlayers, customWordsPerTurn, - clientsPerIPLimit, customWords) + player, lobby, err := game.CreateLobby(desiredLobbyId, playerName, + languageKey, publicLobby, drawingTime, rounds, maxPlayers, + customWordsPerTurn, clientsPerIPLimit, customWords) if err != nil { http.Error(writer, err.Error(), http.StatusBadRequest) return } + // Due to the fact the IDs can be chosen manually, there's a big clash + // potential! However, since we only allow specifying this via the rest API + // we treat this here. This can't be treated in the game package right now + // anyway though, as there'd be an import cycle. + if state.GetLobby(lobby.LobbyID) != nil { + http.Error(writer, "lobby id already in use", http.StatusBadRequest) + return + } + lobby.WriteObject = WriteObject lobby.WritePreparedMessage = WritePreparedMessage player.SetLastKnownAddress(GetIPAddressFromRequest(request)) diff --git a/internal/frontend/create.go b/internal/frontend/create.go index 4e6dc54b..b916263e 100644 --- a/internal/frontend/create.go +++ b/internal/frontend/create.go @@ -8,6 +8,7 @@ import ( "log" "net/http" + "github.com/gofrs/uuid/v5" "github.com/scribble-rs/scribble.rs/internal/api" "github.com/scribble-rs/scribble.rs/internal/config" "github.com/scribble-rs/scribble.rs/internal/game" @@ -171,7 +172,7 @@ func (handler *SSRHandler) ssrCreateLobby(writer http.ResponseWriter, request *h playerName := api.GetPlayername(request) - player, lobby, err := game.CreateLobby(playerName, languageKey, + player, lobby, err := game.CreateLobby(uuid.Nil, playerName, languageKey, publicLobby, drawingTime, rounds, maxPlayers, customWordsPerTurn, clientsPerIPLimit, customWords) if err != nil { diff --git a/internal/game/lobby.go b/internal/game/lobby.go index 4a31e217..ee036036 100644 --- a/internal/game/lobby.go +++ b/internal/game/lobby.go @@ -908,13 +908,17 @@ func (lobby *Lobby) selectWord(wordChoiceIndex int) { // CreateLobby creates a new lobby including the initial player (owner) and // optionally returns an error, if any occurred during creation. func CreateLobby( + desiredLobbyId uuid.UUID, playerName, chosenLanguage string, publicLobby bool, drawingTime, rounds, maxPlayers, customWordsPerTurn, clientsPerIPLimit int, customWords []string, ) (*Player, *Lobby, error) { + if desiredLobbyId == uuid.Nil { + desiredLobbyId = uuid.Must(uuid.NewV4()) + } lobby := &Lobby{ - LobbyID: uuid.Must(uuid.NewV4()).String(), + LobbyID: desiredLobbyId.String(), EditableLobbySettings: EditableLobbySettings{ Rounds: rounds, DrawingTime: drawingTime, diff --git a/internal/state/lobbies_test.go b/internal/state/lobbies_test.go index 99ede65e..2280460f 100644 --- a/internal/state/lobbies_test.go +++ b/internal/state/lobbies_test.go @@ -3,6 +3,7 @@ package state import ( "testing" + "github.com/gofrs/uuid/v5" "github.com/scribble-rs/scribble.rs/internal/config" "github.com/scribble-rs/scribble.rs/internal/game" "github.com/stretchr/testify/require" @@ -13,7 +14,7 @@ func TestAddAndRemove(t *testing.T) { require.Empty(t, lobbies, "Lobbies should be empty when test starts") createLobby := func() *game.Lobby { - player, lobby, err := game.CreateLobby("player", "dutch", true, 100, 10, 10, 3, 1, nil) + player, lobby, err := game.CreateLobby(uuid.Nil, "player", "dutch", true, 100, 10, 10, 3, 1, nil) require.NoError(t, err) lobby.OnPlayerDisconnect(player) return lobby