Files
justthefrax/client.go
Justin C. Miller ad8e570205 Web app first pass
2024-09-03 00:55:35 -07:00

133 lines
2.7 KiB
Go

package main
import (
"encoding/json"
"fmt"
"log"
"time"
"github.com/gorilla/websocket"
)
const (
writeWait = 10 * time.Second // Time allowed to write a message to the peer.
pongWait = 60 * time.Second // Time allowed to read the next pong message from the peer.
pingPeriod = (pongWait * 9) / 10 // Send pings to peer with this period. Must be less than pongWait.
maxMessageSize = 512 // Maximum message size allowed from peer.
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
type Clienter interface {
fmt.Stringer
getClient() *Client
handleMessage(env GameMessage) error
finish()
}
type Client struct {
game *Game
conn *websocket.Conn
outQueue chan []byte
}
func newClient(g *Game, conn *websocket.Conn) Client {
return Client{g, conn, make(chan []byte, 256)}
}
func (c *Client) send(data []byte) bool {
select {
case c.outQueue <- data:
return true
default:
close(c.outQueue)
return false
}
}
func (c *Client) closeQueue() {
close(c.outQueue)
}
func handleError(c Clienter, err error) {
log.Printf("%s error: %v", c, err)
errmsg := makeMessage("error", fmt.Sprintf("%v", err))
c.getClient().send(errmsg)
}
func readPump(c Clienter) {
client := c.getClient()
defer func() {
c.finish()
client.conn.Close()
}()
client.conn.SetReadLimit(maxMessageSize)
client.conn.SetReadDeadline(time.Now().Add(pongWait))
client.conn.SetPongHandler(func(string) error {
client.conn.SetReadDeadline(time.Now().Add(pongWait))
return nil
})
for {
_, data, err := client.conn.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
log.Printf("error: %v", err)
}
break
}
var envelope GameMessage
if err := json.Unmarshal(data, &envelope); err != nil {
handleError(c, fmt.Errorf("Unmarshal: %w", err))
}
if err := c.handleMessage(envelope); err != nil {
handleError(c, err)
}
}
}
func writePump(c Clienter) {
ticker := time.NewTicker(pingPeriod)
client := c.getClient()
defer func() {
ticker.Stop()
client.conn.Close()
}()
for {
select {
case message, ok := <-client.outQueue:
client.conn.SetWriteDeadline(time.Now().Add(writeWait))
if !ok {
// The game closed the channel.
client.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
w, err := client.conn.NextWriter(websocket.TextMessage)
if err != nil {
return
}
w.Write(message)
if err := w.Close(); err != nil {
return
}
case <-ticker.C:
client.conn.SetWriteDeadline(time.Now().Add(writeWait))
if err := client.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
}