This repository has been archived by the owner on Aug 27, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathclient.go
101 lines (77 loc) · 2.83 KB
/
client.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
package main
import (
"code.google.com/p/go.net/websocket"
. "fmt"
)
// Websocket client
type Client struct {
ID string // generated unique id
Name string // a nickname
Lock bool // a lock to prevent the client from editing the active document
Socket *websocket.Conn // websocket for this client
Key string // id of document
}
// JSON format of a client intent, payload and origin
type ClientJSON struct {
Action string // an intent
Data string // payload of intent
Origin string // origin of intent
}
// One can just us &Message directly but I find it all messy when stacked as I do later.
// More so in this case as it is nested.
func (client *Client) mesg(action, data, origin string) *Message {
return &Message{Client: client, JSON: &ClientJSON{Action: action, Data: data, Origin: origin}}
}
// At this point we have a goroutine handling this client who requested this websocket
// The client is considered connected for the remainder of this routine, since this is
// on a goroutine we do not need to fret about blocking.
func WebsocketHandler(ws *websocket.Conn) {
client := &Client{ID: <-Router.GetID, Socket: ws}
// we need a document key before we add this client
var q ClientJSON
if err := websocket.JSON.Receive(client.Socket, &q); err != nil {
return
}
client.Name = q.Origin
client.Key = q.Data
Router.Add <- client
defer func() {
Router.Remove <- client
Router.Echo <- client.mesg("inform", Sprintf("%s disconnected.", client.Name), "Server")
}()
Router.Echo <- client.mesg("inform", Sprintf("%s connected.", client.Name), "Server")
// Request a clean copy of the 'active' document for on behalf of this client.
for peer := range Router.Clients {
if client != peer {
websocket.JSON.Send(peer.Socket, &ClientJSON{Action: "fetch-editor", Origin: client.Name})
break
}
}
for {
var q ClientJSON
if err := websocket.JSON.Receive(client.Socket, &q); err != nil {
break
}
println(Sprintf("%s: `%s` %s => %s", client.Name, client.Key, q.Action, q.Data))
// JSON comes in from a client
// We peek at the Action which is an intent from the client
// and handle it accordingly
switch q.Action {
case "disconnect":
return
case "speech":
Router.Echo <- client.mesg("speech", q.Data, client.Name)
case "lock":
client.Lock = true
Router.Broadcast <- client.mesg("lock", q.Data, client.Name)
case "unlock":
client.Lock = false
Router.Broadcast <- client.mesg("unlock", q.Data, client.Name)
case "update-nick":
client.Name, q.Data = q.Data, client.Name // yea, that just happened
Router.Broadcast <- client.mesg("inform", Sprintf("%s changed nickname to %s", q.Data, client.Name), client.Name)
case "update-editor", "fetch-editor", "update-editor-full":
Router.Broadcast <- client.mesg(q.Action, q.Data, client.Name)
}
}
}