-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathindex.mjs
More file actions
172 lines (151 loc) · 4.57 KB
/
index.mjs
File metadata and controls
172 lines (151 loc) · 4.57 KB
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
export default {
async fetch(request, env) {
let url = new URL(request.url)
let route = url.pathname.slice(1).split('/')[0]
switch (route) {
case 'api':
return handleApiRequest(url.pathname, request, env)
default:
return new Response('notfound ' + route, { status: 404 })
}
},
}
async function checkRoom(perma, env) {
try {
const parts = perma.split('-')
const digest = await crypto.subtle.digest(
{ name: 'SHA-256' },
new TextEncoder().encode(parts[0] + env.DOOM_KEY),
)
const hash = Array.from(new Uint8Array(digest))
const hex = hash
.slice(0, 4)
.map(b => b.toString(16).padStart(2, '0'))
.join('')
return parts[1] == hex ? parts[0] : false
} catch (e) {
return false
}
}
async function createRoom(env) {
const room = env.router.newUniqueId().toString()
const digest = await crypto.subtle.digest(
{ name: 'SHA-256' },
new TextEncoder().encode(room + env.DOOM_KEY),
)
const hash = Array.from(new Uint8Array(digest))
const hex = hash
.slice(0, 4)
.map(b => b.toString(16).padStart(2, '0'))
.join('')
return `${room}-${hex}`
}
async function jsonReply(json, status) {
return new Response(JSON.stringify(json), {
headers: {
'content-type': 'application/json;charset=UTF-8',
'Access-Control-Allow-Origin': '*',
},
status: status,
})
}
async function handleApiRequest(path, request, env) {
let parts = path.slice(1).split('/')
let room = false
switch (parts[1]) {
case 'ws':
case 'room':
room = await checkRoom(parts[2], env)
if (room) {
// in the future this will be our game id
let id = env.router.idFromString(room)
let routerObject = env.router.get(id)
return routerObject.fetch(path, request)
} else {
return jsonReply({ reason: 'invalid room' }, 404)
}
case 'newroom':
room = await createRoom(env)
return jsonReply({ room: room }, 200)
default:
return jsonReply({ reason: 'api not found' }, 404)
}
}
export class Router {
constructor(controller, env) {
this.storage = controller.storage
this.env = env
this.sessions = []
this.gameStarted = false
}
async fetch(request) {
let url = new URL(request.url)
let up = url.pathname.slice(1).split('/')
switch (up[1]) {
case 'room':
var room = await checkRoom(up[2], this.env)
if (room) {
if (up[3] == 'started') {
this.gameStarted = true
}
return jsonReply(
{
room: room,
gameStarted: this.gameStarted,
},
200,
)
} else {
return jsonReply({ reason: 'invalid room' }, 404)
}
case 'ws':
if (request.headers.get('Upgrade') != 'websocket') {
return new Response('expected websocket', { status: 400 })
}
// Get the client's IP address for use with the rate limiter.
// let ip = request.headers.get('CF-Connecting-IP')
const [client, server] = Object.values(new WebSocketPair())
await this.handleSession(server)
return new Response(null, { status: 101, webSocket: client })
}
}
async handleSession(webSocket) {
webSocket.accept()
webSocket.addEventListener('message', async msg => {
try {
let data = msg.data
let from = new Uint32Array(data.slice(4, 8))[0]
let to = new Uint32Array(data.slice(0, 4))[0]
let i
if (from == 1 && to == 0) {
// initial packet from doom server, let's restart
this.sessions.forEach(s => {
s.ws.close(1011, 'closing')
})
this.sessions = []
}
// if it's a new client, add it to the table of clients
if (this.sessions.map(c => c.from).indexOf(from) == -1) {
let session = { ws: webSocket, from: from }
this.sessions.push(session)
}
// send this packet to the corresponding client
i = this.sessions.map(c => c.from).indexOf(to)
if (i != -1) {
this.sessions[i].ws.send(data.slice(4))
}
} catch (err) {
webSocket.send(err.stack)
}
})
// On "close" and "error" events, remove the WebSocket from the clients list
let closeOrErrorHandler = async () => {
i = this.sessions.map(e => e.ws).indexOf(webSocket)
if (i != -1) {
this.sessions.splice(i, 1)
}
}
webSocket.addEventListener('close', closeOrErrorHandler)
webSocket.addEventListener('error', closeOrErrorHandler)
}
}