-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathcursors.ts
119 lines (103 loc) · 2.76 KB
/
cursors.ts
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
import type * as Party from "partykit/server";
export type PartialCursor = {
x: number;
y: number;
pointer: "mouse" | "touch";
};
export type Cursor = PartialCursor & {
country: CountryType;
lastUpdate: number;
};
export type CursorsMap = {
[id: string]: Cursor;
};
type UpdateMessage = {
type: "update";
id: string; // websocket.id
} & PartialCursor;
type SyncMessage = {
type: "sync";
cursors: { [id: string]: Cursor };
};
type RemoveMessage = {
type: "remove";
id: string; // websocket.id
};
type CountryType = string | null;
type CursorState = Partial<Cursor>;
type CursorConnection = Party.Connection<CursorState>;
export default class CursorsServer implements Party.Server {
constructor(public party: Party.Party) {}
onConnect(
connection: CursorConnection,
{ request }: Party.ConnectionContext
) {
const country = (request.cf?.country || null) as CountryType;
// Stash the country in the websocket attachment
connection.setState({
...connection.state,
country,
});
// On connect, send a "sync" message to the new connection
// Pull the cursor from all websocket attachments, excluding self
let cursors = <CursorsMap>{};
for (const socket of this.party.getConnections<CursorState>()) {
const state = socket.state || {};
if (
socket.id !== connection.id &&
state.x !== undefined &&
state.y !== undefined
) {
cursors[socket.id] = {
x: state.x,
y: state.y,
pointer: state.pointer!,
country: state.country!,
lastUpdate: Date.now(),
};
}
}
const msg = <SyncMessage>{
type: "sync",
cursors: cursors,
};
connection.send(JSON.stringify(msg));
}
onMessage(message: string, connection: CursorConnection) {
const position = JSON.parse(message as string);
const state = connection.state;
const cursor = <Cursor>{
x: position.x,
y: position.y,
pointer: position.pointer,
country: state!.country,
lastUpdate: Date.now(),
};
// Stash the cursor in the websocket attachment
connection.setState({
...state,
...cursor,
});
const msg =
position.x && position.y
? <UpdateMessage>{
type: "update",
id: connection.id,
...cursor,
}
: <RemoveMessage>{
type: "remove",
id: connection.id,
};
// Broadcast, excluding self
this.party.broadcast(JSON.stringify(msg), [connection.id]);
}
onClose(connection: CursorConnection) {
// Broadcast a "remove" message to all connections
const msg = <RemoveMessage>{
type: "remove",
id: connection.id,
};
this.party.broadcast(JSON.stringify(msg));
}
}