diff options
Diffstat (limited to 'client.go')
| -rw-r--r-- | client.go | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/client.go b/client.go new file mode 100644 index 0000000..f9453b1 --- /dev/null +++ b/client.go @@ -0,0 +1,166 @@ +// client.go +package main + +import ( + "encoding/json" + "log" + "time" + "unicode/utf8" + + "github.com/gorilla/websocket" +) + +type Client struct { + hub *Hub + conn *websocket.Conn + send chan []byte + token *TokenConfig + nick string + serverID string + playerUUID string + lastSeen time.Time +} + +func (c *Client) readPump() { + defer func() { + c.hub.unregister(c) + c.conn.Close() + }() + + c.conn.SetReadLimit(4096) + c.conn.SetReadDeadline(time.Now().Add(60 * time.Second)) + c.conn.SetPongHandler(func(string) error { + c.conn.SetReadDeadline(time.Now().Add(60 * time.Second)) + return nil + }) + + for { + _, data, err := c.conn.ReadMessage() + if err != nil { + break + } + + var msg IncomingMessage + if err := json.Unmarshal(data, &msg); err != nil { + log.Printf("invalid json from client: %v", err) + continue + } + + switch msg.Type { + case "chat": + c.handleChat(msg) + case "location": + c.handleLocation(msg) + case "nick": + c.handleNick(msg) + default: + log.Printf("unknown message type: %s", msg.Type) + } + } +} + +func (c *Client) writePump() { + ticker := time.NewTicker(30 * time.Second) + defer func() { + ticker.Stop() + c.conn.Close() + }() + + for { + select { + case msg, ok := <-c.send: + c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) + if !ok { + // hub closed the channel + c.conn.WriteMessage(websocket.CloseMessage, []byte{}) + return + } + if err := c.conn.WriteMessage(websocket.TextMessage, msg); err != nil { + return + } + case <-ticker.C: + // ping to keep connection alive + c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second)) + if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil { + return + } + } + } +} + +func (c *Client) handleChat(msg IncomingMessage) { + if msg.Message == "" { + return + } + if utf8.RuneCountInString(msg.Message) > maxMessageLen { + log.Printf("dropping too-long chat message from %s (%s)", c.nick, c.token.Name) + return + } + + ts := time.Now().Unix() + out := OutChat{ + Type: "chat", + FromNick: c.nick, + FromInternal: c.token.Name, + Message: msg.Message, + Timestamp: ts, + } + data, _ := json.Marshal(out) + + // log event + c.hub.addEvent(Event{ + Type: "chat", + Timestamp: ts, + FromNick: c.nick, + FromInternal: c.token.Name, + Message: msg.Message, + }) + + // GLOBAL broadcast + c.hub.broadcastAll(data) +} + +func (c *Client) handleLocation(msg IncomingMessage) { + ts := time.Now().Unix() + out := OutLocation{ + Type: "location", + Nick: c.nick, + Internal: c.token.Name, + ServerID: c.serverID, + X: msg.X, + Y: msg.Y, + Z: msg.Z, + Dimension: msg.Dimension, + Timestamp: ts, + } + data, _ := json.Marshal(out) + + // log event + c.hub.addEvent(Event{ + Type: "location", + Timestamp: ts, + FromNick: c.nick, + FromInternal: c.token.Name, + ServerID: c.serverID, + X: msg.X, + Y: msg.Y, + Z: msg.Z, + Dimension: msg.Dimension, + }) + + // broadcast only to same server + c.hub.broadcastToServer(c.serverID, data) +} + +func (c *Client) handleNick(msg IncomingMessage) { + if msg.Nick == "" { + return + } + if utf8.RuneCountInString(msg.Nick) > maxNickLen { + log.Printf("rejecting nick > %d chars from %s (%s)", maxNickLen, c.nick, c.token.Name) + return + } + c.hub.mu.Lock() + c.nick = msg.Nick + c.hub.mu.Unlock() +}
\ No newline at end of file |
