forked from gophergala2016/spacegophers
/
user.go
122 lines (100 loc) · 2.64 KB
/
user.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package main
import (
"time"
"github.com/apex/log"
"github.com/gorilla/websocket"
"github.com/xtgo/uuid"
)
const (
// Time allowed to write a message to the peer.
writeWait = 20 * time.Second
// Time allowed to read the next pong message from the peer.
pongWait = 20 * time.Second
// Send pings to peer with this period. Must be less than pongWait.
pingPeriod = (pongWait * 9) / 10
// Maximum message size allowed from peer.
maxMessageSize = 512
)
// NewUser creates a new user with a new Gopher to manage the user's new ws
// connection.
func NewUser(ctx log.Interface, ws *websocket.Conn) User {
id := uuid.NewRandom().String()[:3]
return User{
ID: id,
Gopher: NewGopher(id, RandomCoordinates(boardSize)),
Log: ctx.WithField("module", "User"),
send: make(chan []byte, 256),
ws: ws,
}
}
// User contains all the details that a user needs to play!
type User struct {
ID string
Log log.Interface
Gopher Gopher
g *Game
ws *websocket.Conn
send chan []byte // Buffered channel of outbound messages.
}
// readPump pumps messages from the websocket connection to the hub.
func (u *User) readPump() {
defer func() {
u.g.unregister <- u
u.ws.Close()
}()
u.ws.SetReadLimit(maxMessageSize)
u.ws.SetReadDeadline(time.Now().Add(pongWait))
u.ws.SetPongHandler(func(string) error {
u.ws.SetReadDeadline(time.Now().Add(pongWait))
return nil
})
for {
_, message, err := u.ws.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
// TODO: do something??
}
u.Log.Error(err.Error())
return
}
u.Log.WithField("content", string(message)).Debug("new message")
c := NewCommand(u, string(message))
u.g.commands <- c
}
}
// write writes a message with the given message type and payload.
func (u *User) write(mt int, payload []byte) error {
u.ws.SetWriteDeadline(time.Now().Add(writeWait))
return u.ws.WriteMessage(mt, payload)
}
// writePump pumps messages from the hub to the websocket connection.
func (u *User) writePump() {
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
u.ws.Close()
}()
for {
select {
case message, ok := <-u.send:
if !ok {
u.Log.Debug("send channel closed")
u.write(websocket.CloseMessage, []byte{})
return
}
if err := u.write(websocket.TextMessage, message); err != nil {
u.Log.WithError(err).Debug("can't write message to ws")
return
}
case <-ticker.C:
if err := u.write(websocket.PingMessage, []byte{}); err != nil {
u.Log.WithError(err).Debug("can't write ping to ws")
return
}
}
}
}
func (u *User) run() {
go u.writePump()
u.readPump()
}