From e67970f45f74d1369ca9c278dbb0d17993b2cfd2 Mon Sep 17 00:00:00 2001 From: Jakub Czyz Date: Wed, 30 Jul 2025 11:09:41 +0200 Subject: [PATCH 1/6] add http input handler --- go.mod | 4 +- go.sum | 2 + http_input_handler.go | 96 +++++++++++++++++++++++++++++++++++++++++++ main.go | 4 ++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 http_input_handler.go diff --git a/go.mod b/go.mod index 5053716..e6d7694 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module papergo go 1.24.5 require ( - golang.org/x/sys v0.34.0 // indirect + github.com/google/uuid v1.6.0 golang.org/x/term v0.33.0 ) + +require golang.org/x/sys v0.34.0 // indirect diff --git a/go.sum b/go.sum index 5720d35..5181155 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= diff --git a/http_input_handler.go b/http_input_handler.go new file mode 100644 index 0000000..6a5f121 --- /dev/null +++ b/http_input_handler.go @@ -0,0 +1,96 @@ +package main + +import ( + "context" + "errors" + "fmt" + "net/http" + + "github.com/google/uuid" +) + +const frontend = ` + + +
+ + +
+
+ + +
+ +` + +type HttpInputHandler struct { + playerService PlayerService + server *http.Server + playerIdMap map[uuid.UUID]int +} + +func NewHttpInputHandler(ps PlayerService) *HttpInputHandler { + handler := &HttpInputHandler{ + playerService: ps, + playerIdMap: map[uuid.UUID]int{}, + } + + return handler +} + +func (k *HttpInputHandler) Listen(addr string) { + k.server = &http.Server{ + Addr: addr, + Handler: k, + } + k.server.ListenAndServe() +} + +func (h *HttpInputHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + userId := h.authenticate(w, req) + playerId, ok := h.playerIdMap[userId] + if !ok { + playerId = h.playerService.Join() + h.playerIdMap[userId] = playerId + } + + action := req.FormValue("action") + switch action { + case "left": + h.playerService.TurnLeft(playerId) + case "right": + h.playerService.TurnRight(playerId) + } + + fmt.Fprint(w, frontend) + +} + +func (h *HttpInputHandler) authenticate(w http.ResponseWriter, req *http.Request) uuid.UUID { + cookie, err := req.Cookie("userId") + if errors.Is(err, http.ErrNoCookie) { + return h.setNewUserId(w) + } + uid, err := uuid.Parse(cookie.Value) + if err != nil { + return h.setNewUserId(w) + } + return uid +} + +func (h *HttpInputHandler) setNewUserId(w http.ResponseWriter) uuid.UUID { + userId := uuid.New() + http.SetCookie(w, &http.Cookie{ + Name: "userId", + Value: userId.String(), + Secure: true, + }) + return userId +} + +func (k *HttpInputHandler) Close() { + if k.server == nil { + return + } + k.server.Shutdown(context.Background()) +} diff --git a/main.go b/main.go index 2a38454..d4fbfb7 100644 --- a/main.go +++ b/main.go @@ -7,5 +7,9 @@ func main() { go keyboard.Listen() defer keyboard.Close() + httpServer := NewHttpInputHandler(game) + defer httpServer.Close() + go httpServer.Listen(":8080") + game.Run() } From d5f1655faa1650f09c53c1e40ea7a2702cb57478 Mon Sep 17 00:00:00 2001 From: Jakub Czyz Date: Thu, 31 Jul 2025 15:17:12 +0200 Subject: [PATCH 2/6] improve frontend rendering --- http_input_handler.go | 94 +++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/http_input_handler.go b/http_input_handler.go index 6a5f121..22c922c 100644 --- a/http_input_handler.go +++ b/http_input_handler.go @@ -10,17 +10,64 @@ import ( ) const frontend = ` - - -
- - -
-
- - -
- + + + + + + Full Screen Buttons + + + + +
+
+ + +
+
+ + +
+
+ ` type HttpInputHandler struct { @@ -47,12 +94,8 @@ func (k *HttpInputHandler) Listen(addr string) { } func (h *HttpInputHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - userId := h.authenticate(w, req) - playerId, ok := h.playerIdMap[userId] - if !ok { - playerId = h.playerService.Join() - h.playerIdMap[userId] = playerId - } + playerId := h.authenticate(w, req) + h.playerService.Join(playerId) action := req.FormValue("action") switch action { @@ -67,25 +110,24 @@ func (h *HttpInputHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } func (h *HttpInputHandler) authenticate(w http.ResponseWriter, req *http.Request) uuid.UUID { - cookie, err := req.Cookie("userId") + cookie, err := req.Cookie("playerId") if errors.Is(err, http.ErrNoCookie) { - return h.setNewUserId(w) + return h.setNewPlayerId(w) } uid, err := uuid.Parse(cookie.Value) if err != nil { - return h.setNewUserId(w) + return h.setNewPlayerId(w) } return uid } -func (h *HttpInputHandler) setNewUserId(w http.ResponseWriter) uuid.UUID { - userId := uuid.New() +func (h *HttpInputHandler) setNewPlayerId(w http.ResponseWriter) uuid.UUID { + playerId := uuid.New() http.SetCookie(w, &http.Cookie{ - Name: "userId", - Value: userId.String(), - Secure: true, + Name: "playerId", + Value: playerId.String(), }) - return userId + return playerId } func (k *HttpInputHandler) Close() { From dd5c1ed2a4947afcf7615ecad99048e3ca035816 Mon Sep 17 00:00:00 2001 From: Jakub Czyz Date: Fri, 1 Aug 2025 00:24:23 +0200 Subject: [PATCH 3/6] rename existing input_handler to keyboard_input_handler --- input_handler.go => keyboard_input_handler.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename input_handler.go => keyboard_input_handler.go (100%) diff --git a/input_handler.go b/keyboard_input_handler.go similarity index 100% rename from input_handler.go rename to keyboard_input_handler.go From c0981d687b7557959e5cdc7b84cd376242ceddcb Mon Sep 17 00:00:00 2001 From: Jakub Czyz Date: Sat, 2 Aug 2025 11:22:47 +0200 Subject: [PATCH 4/6] add websocket connection --- go.mod | 5 +- go.sum | 2 + http_input_handler.go | 134 ++++++++++++++---------------------------- index.html | 58 ++++++++++++++++++ 4 files changed, 107 insertions(+), 92 deletions(-) create mode 100644 index.html diff --git a/go.mod b/go.mod index e6d7694..2ca4644 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,7 @@ require ( golang.org/x/term v0.33.0 ) -require golang.org/x/sys v0.34.0 // indirect +require ( + github.com/gorilla/websocket v1.5.3 // indirect + golang.org/x/sys v0.34.0 // indirect +) diff --git a/go.sum b/go.sum index 5181155..7b1422c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= diff --git a/http_input_handler.go b/http_input_handler.go index 22c922c..9033619 100644 --- a/http_input_handler.go +++ b/http_input_handler.go @@ -2,77 +2,28 @@ package main import ( "context" - "errors" - "fmt" + "log" "net/http" + _ "embed" + "github.com/google/uuid" + "github.com/gorilla/websocket" ) -const frontend = ` - - - - - - Full Screen Buttons - - - - -
-
- - -
-
- - -
-
- -` +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true // Allow all connections + }, +} type HttpInputHandler struct { playerService PlayerService server *http.Server + mux *http.ServeMux playerIdMap map[uuid.UUID]int } @@ -80,54 +31,55 @@ func NewHttpInputHandler(ps PlayerService) *HttpInputHandler { handler := &HttpInputHandler{ playerService: ps, playerIdMap: map[uuid.UUID]int{}, + + mux: http.NewServeMux(), } + handler.mux.HandleFunc("GET /", handler.serveFrontend) + handler.mux.HandleFunc("GET /ws", handler.serveWebsocket) + return handler } func (k *HttpInputHandler) Listen(addr string) { k.server = &http.Server{ Addr: addr, - Handler: k, + Handler: k.mux, } k.server.ListenAndServe() } -func (h *HttpInputHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - playerId := h.authenticate(w, req) - h.playerService.Join(playerId) - - action := req.FormValue("action") - switch action { - case "left": - h.playerService.TurnLeft(playerId) - case "right": - h.playerService.TurnRight(playerId) - } - - fmt.Fprint(w, frontend) - +func (h *HttpInputHandler) serveFrontend(w http.ResponseWriter, req *http.Request) { + w.Write(frontend) } -func (h *HttpInputHandler) authenticate(w http.ResponseWriter, req *http.Request) uuid.UUID { - cookie, err := req.Cookie("playerId") - if errors.Is(err, http.ErrNoCookie) { - return h.setNewPlayerId(w) - } - uid, err := uuid.Parse(cookie.Value) +func (h *HttpInputHandler) serveWebsocket(w http.ResponseWriter, req *http.Request) { + conn, err := upgrader.Upgrade(w, req, nil) if err != nil { - return h.setNewPlayerId(w) + log.Println("Error upgrading connection:", err) + return } - return uid -} + defer conn.Close() -func (h *HttpInputHandler) setNewPlayerId(w http.ResponseWriter) uuid.UUID { playerId := uuid.New() - http.SetCookie(w, &http.Cookie{ - Name: "playerId", - Value: playerId.String(), - }) - return playerId + h.playerService.Join(playerId) + + for { + // Read a message from the client + _, message, err := conn.ReadMessage() + if err != nil { + log.Println("Error reading message:", err) + break + } + + switch string(message) { + case "left": + h.playerService.TurnLeft(playerId) + case "right": + h.playerService.TurnRight(playerId) + } + conn.WriteMessage(websocket.TextMessage, []byte("response")) + } } func (k *HttpInputHandler) Close() { diff --git a/index.html b/index.html new file mode 100644 index 0000000..8e04601 --- /dev/null +++ b/index.html @@ -0,0 +1,58 @@ + + + + + + Full Screen Buttons + + + +
+ + +
+ + + \ No newline at end of file From f52e3d98cf39a3e2bd1d68852480ea4b66910969 Mon Sep 17 00:00:00 2001 From: Jakub Czyz Date: Sat, 2 Aug 2025 18:05:56 +0200 Subject: [PATCH 5/6] send keepalive websocket messages --- http_input_handler.go | 10 ++++------ index.html | 35 ++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/http_input_handler.go b/http_input_handler.go index 9033619..7ab91da 100644 --- a/http_input_handler.go +++ b/http_input_handler.go @@ -65,20 +65,18 @@ func (h *HttpInputHandler) serveWebsocket(w http.ResponseWriter, req *http.Reque h.playerService.Join(playerId) for { - // Read a message from the client - _, message, err := conn.ReadMessage() + _, msg, err := conn.ReadMessage() if err != nil { log.Println("Error reading message:", err) break } - switch string(message) { - case "left": + switch string(msg) { + case "l": h.playerService.TurnLeft(playerId) - case "right": + case "r": h.playerService.TurnRight(playerId) } - conn.WriteMessage(websocket.TextMessage, []byte("response")) } } diff --git a/index.html b/index.html index 8e04601..0c5b7c0 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + Full Screen Buttons
- - + +
\ No newline at end of file From 5efab529d30a9243f460c0d68119564269acd3e1 Mon Sep 17 00:00:00 2001 From: Jan Sierpina Date: Sun, 3 Aug 2025 01:55:30 +0200 Subject: [PATCH 6/6] remove comments, logs --- http_input_handler.go | 2 +- renderer.go | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/http_input_handler.go b/http_input_handler.go index 7ab91da..08ca5e7 100644 --- a/http_input_handler.go +++ b/http_input_handler.go @@ -16,7 +16,7 @@ var frontend []byte var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { - return true // Allow all connections + return true }, } diff --git a/renderer.go b/renderer.go index efa9347..552ce95 100644 --- a/renderer.go +++ b/renderer.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "log" "os" "golang.org/x/term" @@ -109,8 +108,6 @@ func NewRenderer() *Renderer { if width > height && width > 100 { gameCols -= scoreboardWidth scoreboard = true - } else { - log.Println("Terminal too small for scoreboard, hiding it.") } r := Renderer{ @@ -175,7 +172,6 @@ func (r *Renderer) bufferScoreboard(g *Game, b [][]character) { } for i, p := range scoreboard { - log.Println(p.Id) name := PlayerNames[p.Id] scoreText := fmt.Sprintf("%s: %d", name, 100) for j, char := range scoreText {