diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index e046d09..eaff965 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -20,6 +20,7 @@ func (h *Handler) Auth(w http.ResponseWriter, r *http.Request) { } func (h *Handler) Callback(w http.ResponseWriter, r *http.Request) { + fmt.Println("yo we in the callback bro") user, err := gothic.CompleteUserAuth(w, r) if err != nil { diff --git a/internal/handlers/home.go b/internal/handlers/home.go index c15551c..9671e10 100644 --- a/internal/handlers/home.go +++ b/internal/handlers/home.go @@ -112,11 +112,15 @@ func (h *Handler) WsHandler(w http.ResponseWriter, r *http.Request) { for { _, messageBytes, err := conn.ReadMessage() - if err != nil { + if err != nil && !websocket.IsCloseError( + err, + websocket.CloseNormalClosure, + websocket.CloseGoingAway, + websocket.CloseNoStatusReceived, + ) { fmt.Printf("error reading message: %v", err) } - fmt.Printf("message being broadcasted: %v", string(messageBytes)) Hub.Broadcast <- messageBytes } } diff --git a/main.go b/main.go index 5931fe7..2e73d81 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "flag" "fmt" "net/http" "os" @@ -18,6 +19,9 @@ import ( ) func main() { + dev := flag.Bool("dev", true, "is this a dev env?") + flag.Parse() + err := godotenv.Load() if err != nil { fmt.Printf("error loading .env file: %v", err) @@ -33,7 +37,7 @@ func main() { twitch.New( os.Getenv("TWITCH_CLIENT_ID"), os.Getenv("TWITCH_SECRET"), - "https://jellyfish-app-cisrn.ondigitalocean.app/auth/twitch/callback", + os.Getenv("TWITCH_CALLBACK"), ), ) @@ -57,9 +61,16 @@ func main() { } go func() { - fmt.Println("Server started...") - if err := server.ListenAndServe(); err != http.ErrServerClosed { - fmt.Printf("Serve error: %v\n", err) + if *dev { + fmt.Println("Dev server started...") + if err := server.ListenAndServe(); err != http.ErrServerClosed { + fmt.Printf("Serve error: %v\n", err) + } + } else { + fmt.Println("Server started...") + if err := server.ListenAndServeTLS("server.crt", "server.key"); err != http.ErrServerClosed { + fmt.Printf("Serve error: %v\n", err) + } } }() diff --git a/static/script.js b/static/script.js index f69ddfa..3d96a37 100644 --- a/static/script.js +++ b/static/script.js @@ -24,6 +24,7 @@ window.addEventListener("DOMContentLoaded", function() { this.crouching = false; this.isPunching = false; this.punchHasHit = false; + this.sentJoin = false; } @@ -63,6 +64,8 @@ window.addEventListener("DOMContentLoaded", function() { if (this.hitFrame > 15) { this.action = 'idle'; this.hitFrame = 0; + this.isPunching = false; + this.punchHasHit = false; } } @@ -174,6 +177,15 @@ window.addEventListener("DOMContentLoaded", function() { ctx.fillText(lines[i], bubbleX + padding, bubbleY + padding + i * lineHeight); } } + + // name tag + ctx.save(); + ctx.font = "14px sans-serif"; + ctx.textAlign = "center"; + ctx.textBaseline = "bottom"; + ctx.fillStyle = "black"; + ctx.fillText(this.id, x, y - 12); + ctx.restore(); } punch() { @@ -270,10 +282,21 @@ window.addEventListener("DOMContentLoaded", function() { this._onKeyUp = this._onKeyUp.bind(this); this._onSendClick = this._onSendClick.bind(this); this._leaveGame = this._leaveGame.bind(this); - } + this._started = false; + this._sentJoin = false; + this._stopped = false; + + this._reconnectDelay = 1000; + this._maxReconnectDelay = 15000; + this._reconnectTimer = null; + } start() { + if (this._started) return; + this.started = true; + this._stopped = false; + document.addEventListener("keydown", this._onKeyDown); document.addEventListener("keyup", this._onKeyUp); if (this.sendBtn) this.sendBtn.addEventListener("click", this._onSendClick); @@ -286,12 +309,36 @@ window.addEventListener("DOMContentLoaded", function() { } destroy() { + this._stopped = true; + this._started = false; + if (this._reconnectTimer) { + clearTimeout(this._reconnectTimer); + this._reconnectTimer = null; + } document.removeEventListener("keydown", this._onKeyDown); document.removeEventListener("keyup", this._onKeyUp); if (this.sendBtn) this.sendBtn.removeEventListener("click", this._onSendClick); if (this.ws) this.ws.close(); } + _scheduleReconnect() { + if (this._stopped) return; + if (this._reconnectTimer) { + this._reconnectTimer = null; + } + + const jitter = this._reconnectDelay * (Math.random() * 0.4 - 0.2); + const delay = Math.max(250, Math.min(this._maxReconnectDelay, this._reconnectDelay + jitter)); + console.log(`Reconnecting websocket...`) + + this._reconnectTimer = setTimeout(() => { + this._reconnectTimer = null; + this._initWebSocket(); + }, delay); + + this._reconnectDelay = Math.min(this._maxReconnectDelay, Math.round(this._reconnectDelay * 1.8)); + } + loop() { this.time += 0.1; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); @@ -312,12 +359,12 @@ window.addEventListener("DOMContentLoaded", function() { const playerABody = playerA.getBodyHitbox(); const playerBBody = playerB.getBodyHitbox(); - if (playerAPunch && rectangleOverlap(playerAPunch, playerBBody && !playerA.punchHasHit)) { + if (playerAPunch && !playerA.punchHasHit && rectangleOverlap(playerAPunch, playerBBody)) { playerA.punchHasHit = true; console.log("Player A hits player B"); } - if (playerBPunch && rectangleOverlap(playerBPunch, playerABody) && !playerB.punchHasHit) { + if (playerBPunch && !playerB.punchHasHit && rectangleOverlap(playerBPunch, playerABody)) { playerB.punchHasHit = true; console.log("Player B hits player A") } @@ -353,16 +400,16 @@ window.addEventListener("DOMContentLoaded", function() { me.punch() this._sendAction('e', "keyDown") break; - case 'k': + case 'w': me.jump(); this._sendAction('k', "keyDown") break; - case 'j': + case 's': me.crouching = true; this._sendAction('j', 'keyDown') break; - case 'h': - case 'l': + case 'a': + case 'd': if (!me.keys) me.keys = {}; if (!me.keys[e.key]) me.keys[e.key] = true; this._sendAction(e.key, 'keyDown') @@ -380,12 +427,16 @@ window.addEventListener("DOMContentLoaded", function() { const me = this._me(); if (!me) return; - if (e.key === 'j') { + if (e.key === 't') { + this.chatInput.focus(); + } + + if (e.key === 's') { me.crouching = false; this._sendAction('j', 'keyUp') } - if (e.key === 'h' || e.key === 'l') { + if (e.key === 'a' || e.key === 'd') { if (!me.keys) me.keys = {}; me.keys[e.key] = false; this._sendAction(e.key, 'keyUp') @@ -423,52 +474,68 @@ window.addEventListener("DOMContentLoaded", function() { } _initWebSocket() { + if (this._stopped) return; + + if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { + return; + } const scheme = location.protocol === "https:" ? "wss://" : "ws://"; this.ws = new WebSocket(scheme + location.host + "/ws"); this.ws.onopen = () => { - if (this.pageData.username !== "") { + console.log('i am opening') + this._reconnectDelay = 1000; + + if (!this._sentJoin && this.pageData.username !== "") { this._send({ Id: this.pageData.username, Action: "join", Message: "" }); + this._sentJoin = true; + } }; this.ws.onmessage = (e) => { - const data = JSON.parse(e.data); - this._ensurePlayer(data.Id); - - // ignore echo - if (data.Id === this.pageData.username) return; - - const p = this.players[data.Id]; - switch (data.Action) { - case "t": - p.talk(data.Message); - break; - case "e": - p.punch(); - break; - case "k": - p.jump(); - break; - case "h": - case "l": - p.keys[data.Action] = data.ActionType === "keyDown"; - break; + if (e.data) { + const data = JSON.parse(e.data); + this._ensurePlayer(data.Id); + + // ignore echo + if (data.Id === this.pageData.username) return; + + const p = this.players[data.Id]; + switch (data.Action) { + case "t": + p.talk(data.Message); + break; + case "e": + p.punch(); + break; + case "k": + p.jump(); + break; + case "h": + case "l": + p.keys[data.Action] = data.ActionType === "keyDown"; + break; + } } }; this.ws.onerror = (err) => { console.log("ws error:", err); + this._scheduleReconnect() }; - this.ws.onclose = () => { + this.ws.onclose = (event) => { + console.log(event.code) console.log("leaving game...") console.log(this.pageData) - delete this.players[this.pageData.username] + this.ws = null; + this._sentJoin = false; + this._scheduleReconnect() } } diff --git a/tmp/build-errors.log b/tmp/build-errors.log index 4f87878..7582694 100644 --- a/tmp/build-errors.log +++ b/tmp/build-errors.log @@ -1 +1 @@ -exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file +exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file diff --git a/tmp/main b/tmp/main index 3f6e09c..70dcad7 100755 Binary files a/tmp/main and b/tmp/main differ diff --git a/todo.md b/todo.md index 25cf328..482ce9c 100644 --- a/todo.md +++ b/todo.md @@ -1,12 +1,9 @@ -- move down to 1 container -- enter to send chat message -- show name plate - fix sending empty messages -- focus chat on 't' -- show controls on the page - hit points - death - weapons from channel point - - https://dev.twitch.tv/docs/eventsub/ - jetpack - remap wasd +- allow users to set their own keybindings +- map attack to mouse click as well