From fc088a04fe47c9bae7404ef2f0e03713ba2f0fc7 Mon Sep 17 00:00:00 2001 From: Marco Date: Sun, 12 May 2024 15:36:30 +0200 Subject: [PATCH] More work for introducing a new game handler, also: ratelimiter --- api/handler/handler.go | 68 +++++++++++++++++++++---------------- api/handler/handler_test.go | 2 +- api/websocket/connection.go | 2 +- go.mod | 3 ++ go.sum | 4 +++ lobby_registry/lobby.go | 58 ------------------------------- lobby_registry/registry.go | 61 --------------------------------- main.go | 5 ++- usher/usher.go | 45 ------------------------ 9 files changed, 49 insertions(+), 199 deletions(-) delete mode 100644 lobby_registry/lobby.go delete mode 100644 lobby_registry/registry.go delete mode 100644 usher/usher.go diff --git a/api/handler/handler.go b/api/handler/handler.go index 6c661e0..b7382a1 100644 --- a/api/handler/handler.go +++ b/api/handler/handler.go @@ -1,24 +1,26 @@ package handler import ( - "log" "mchess_server/api" "mchess_server/chess" - lobbies "mchess_server/lobby_registry" - "mchess_server/usher" + "mchess_server/lobbies" "mchess_server/utils" "net/http" "sync" "github.com/gin-gonic/gin" "github.com/google/uuid" + "go.uber.org/ratelimit" ) var mut sync.Mutex +var limiter = ratelimit.New(10) + +func HostGameHandler(c *gin.Context) { + limiter.Take() -func HostPrivateGameHandler(c *gin.Context) { player := chess.NewPlayer(uuid.New()) - u := usher.GetUsher() + u := lobbies.GetUsher() mut.Lock() defer mut.Unlock() @@ -36,6 +38,8 @@ func HostPrivateGameHandler(c *gin.Context) { } func GetLobbyForPassphraseHandler(c *gin.Context) { + limiter.Take() + reqPassphrase := c.Param("phrase") if reqPassphrase == "" { c.IndentedJSON(http.StatusBadRequest, reqPassphrase) @@ -57,34 +61,17 @@ func GetLobbyForPassphraseHandler(c *gin.Context) { c.IndentedJSON(http.StatusOK, lobbyInfo) } -func RegisterForRandomGame(c *gin.Context) { - player := chess.NewPlayer(uuid.New()) - usher := usher.GetUsher() - - mut.Lock() - defer mut.Unlock() - lobby := usher.WelcomeNewPlayer(player) - usher.AddPlayerToLobbyAndStartGameIfFull(player, lobby) - - info := api.PlayerInfo{ - PlayerID: &player.Uuid, - LobbyID: &lobby.Uuid, - } - log.Println("responding with info ", info) - - c.Header("Access-Control-Allow-Origin", "*") - c.IndentedJSON(http.StatusOK, info) -} - +// TODO: this will be replaced by the JoinGameHandler() func JoinPrivateGame(c *gin.Context) { + limiter.Take() + req := api.PlayerInfo{} - log.Println(c.Request.Body) err := c.ShouldBindJSON(&req) if err != nil || req.Passphrase == nil || *req.Passphrase == "" { c.IndentedJSON(http.StatusNotFound, req) } - u := usher.GetUsher() + u := lobbies.GetUsher() if req.Passphrase != nil && *req.Passphrase != "" && @@ -127,8 +114,29 @@ func JoinPrivateGame(c *gin.Context) { c.IndentedJSON(http.StatusOK, info) } -func JoinGame(c *gin.Context) { - gameID := c.Param("id") - log.Println(gameID) - c.JSON(http.StatusOK, gameID) +func JoinGameHandler(c *gin.Context) { + limiter.Take() + + id := c.Param("id") + idAsUUID, err := uuid.Parse(id) + if err != nil { + c.IndentedJSON(http.StatusBadRequest, nil) + return + } + + passphrase := api.Passphrase{} + c.ShouldBindJSON(&passphrase) + + u := lobbies.GetUsher() + lobby := u.GetLobbyByID(idAsUUID) + if lobby == nil { + c.IndentedJSON(http.StatusNotFound, nil) + return + } + + lobbyInfo := api.LobbyInfo{ + ID: &lobby.Uuid, + } + + c.IndentedJSON(http.StatusOK, lobbyInfo) } diff --git a/api/handler/handler_test.go b/api/handler/handler_test.go index 8e69445..ff5fead 100644 --- a/api/handler/handler_test.go +++ b/api/handler/handler_test.go @@ -20,7 +20,7 @@ func Test_GetLobbyFromPassphraseHandler(t *testing.T) { t.Run("host a lobby", func(t *testing.T) { r1 := httptest.NewRecorder() ctx1, e1 := gin.CreateTestContext(r1) - e1.GET("/api/hostPrivate", HostPrivateGameHandler) + e1.GET("/api/hostPrivate", HostGameHandler) hostGameRequest, _ := http.NewRequest("GET", "/api/hostPrivate", nil) ctx1.Request = hostGameRequest diff --git a/api/websocket/connection.go b/api/websocket/connection.go index 464303c..1f18929 100644 --- a/api/websocket/connection.go +++ b/api/websocket/connection.go @@ -8,7 +8,7 @@ import ( "mchess_server/api" "net/http" - lobbies "mchess_server/lobby_registry" + "mchess_server/lobbies" "github.com/gin-gonic/gin" gorillaws "github.com/gorilla/websocket" diff --git a/go.mod b/go.mod index 38fda34..3f129d6 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,8 @@ require ( github.com/stretchr/testify v1.9.0 ) +require github.com/benbjohnson/clock v1.3.0 // indirect + require ( github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect @@ -32,6 +34,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + go.uber.org/ratelimit v0.3.1 golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect diff --git a/go.sum b/go.sum index 51dc3b8..d95f318 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= @@ -69,6 +71,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= +go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= diff --git a/lobby_registry/lobby.go b/lobby_registry/lobby.go deleted file mode 100644 index d900002..0000000 --- a/lobby_registry/lobby.go +++ /dev/null @@ -1,58 +0,0 @@ -package lobby_registry - -import ( - "mchess_server/chess" - "mchess_server/utils" - - "github.com/google/uuid" -) - -type Lobby struct { - Uuid uuid.UUID - Game *chess.Game - PlayerJoined chan bool - Passphrase utils.Passphrase -} - -func NewEmptyLobbyWithUUID(uuid uuid.UUID) *Lobby { - return &Lobby{ - Uuid: uuid, - Game: chess.NewGame(), - PlayerJoined: make(chan bool), - } -} - -func newEmptyLobbyWithPassphrase() *Lobby { - lobby := NewEmptyLobbyWithUUID(uuid.New()) - lobby.Passphrase = utils.NewPassphrase() - - return lobby -} - -func (l *Lobby) AddPlayerAndStartGameIfFull(player *chess.Player) { - l.Game.AddPlayersToGame(player) - if l.IsFull() { - l.Game.StartHandling() - } -} - -func (w *Lobby) IsFull() bool { - return len(w.Game.GetPlayers()) == 2 -} - -func (l *Lobby) GetPlayerByUUID(uuid uuid.UUID) (*chess.Player, bool) { - for _, player := range l.Game.GetPlayers() { - if player.Uuid == uuid { - return player, true - } - } - return nil, false -} - -func (l *Lobby) GetPlayer1() *chess.Player { - return l.Game.GetPlayer1() -} - -func (l *Lobby) GetPlayer2() *chess.Player { - return l.Game.GetPlayer2() -} diff --git a/lobby_registry/registry.go b/lobby_registry/registry.go deleted file mode 100644 index 1412462..0000000 --- a/lobby_registry/registry.go +++ /dev/null @@ -1,61 +0,0 @@ -package lobby_registry - -import ( - "mchess_server/utils" - - "github.com/google/uuid" -) - -type LobbyRegistry struct { - lobbies map[uuid.UUID]*Lobby -} - -var instance *LobbyRegistry - -func GetLobbyRegistry() *LobbyRegistry { - if instance == nil { - instance = newLobbyRegistry() - } - - return instance -} - -func newLobbyRegistry() *LobbyRegistry { - return &LobbyRegistry{lobbies: make(map[uuid.UUID]*Lobby)} -} - -func (r *LobbyRegistry) CreateNewPrivateLobby() *Lobby { - lobby := newEmptyLobbyWithPassphrase() - r.addNewLobby(lobby) - return lobby -} - -func (r *LobbyRegistry) GetLobbyForPlayer() *Lobby { - for _, lobby := range r.lobbies { - if !lobby.IsFull() { - return lobby - } - } - - newLobby := NewEmptyLobbyWithUUID(uuid.New()) - r.addNewLobby(newLobby) - return newLobby -} - -func (r *LobbyRegistry) GetLobbyByUUID(uuid uuid.UUID) *Lobby { - return r.lobbies[uuid] -} - -func (r *LobbyRegistry) GetLobbyByPassphrase(p utils.Passphrase) *Lobby { - for _, lobby := range r.lobbies { - if lobby.Passphrase == p { - return lobby - } - } - return nil -} - -func (r *LobbyRegistry) addNewLobby(lobby *Lobby) uuid.UUID { - r.lobbies[lobby.Uuid] = lobby - return lobby.Uuid -} diff --git a/main.go b/main.go index 3d5837c..ab54f4a 100644 --- a/main.go +++ b/main.go @@ -24,14 +24,13 @@ func main() { } router := gin.Default() - router.GET("/api/random", handler.RegisterForRandomGame) - router.GET("/api/hostPrivate", handler.HostPrivateGameHandler) + router.GET("/api/hostPrivate", handler.HostGameHandler) router.POST("/api/joinPrivate", handler.JoinPrivateGame) router.GET("/api/ws", websocket.RegisterWebSocketConnection) router.GET("/api/getLobbyForPassphrase/:phrase", handler.GetLobbyForPassphraseHandler) router.GET("/api/registerWsForGame/:id", websocket.ConnectWsForGame) - router.POST("/api/joinGame/:id", handler.JoinGame) + router.POST("/api/joinGame/:id", handler.JoinGameHandler) if debugMode { log.Println("Starting service WITHOUT TLS") diff --git a/usher/usher.go b/usher/usher.go deleted file mode 100644 index cfbbd51..0000000 --- a/usher/usher.go +++ /dev/null @@ -1,45 +0,0 @@ -package usher - -import ( - "mchess_server/chess" - lobbies "mchess_server/lobby_registry" - "mchess_server/utils" -) - -type Usher struct { -} - -var instance *Usher - -func newUsher() *Usher { - return &Usher{} -} - -func GetUsher() *Usher { - if instance == nil { - instance = newUsher() - } - return instance -} - -func (u *Usher) WelcomeNewPlayer(player *chess.Player) *lobbies.Lobby { - lobby := lobbies.GetLobbyRegistry().GetLobbyForPlayer() - return lobby -} - -func (u *Usher) CreateNewPrivateLobby(player *chess.Player) *lobbies.Lobby { - lobby := lobbies.GetLobbyRegistry().CreateNewPrivateLobby() - return lobby -} - -func (u *Usher) FindExistingPrivateLobby(p utils.Passphrase) *lobbies.Lobby { - lobby := lobbies.GetLobbyRegistry().GetLobbyByPassphrase(p) - if lobby == nil || lobby.IsFull() { - return nil - } - return lobby -} - -func (u *Usher) AddPlayerToLobbyAndStartGameIfFull(player *chess.Player, lobby *lobbies.Lobby) { - lobby.AddPlayerAndStartGameIfFull(player) -}