-
Notifications
You must be signed in to change notification settings - Fork 1
/
server.js
144 lines (117 loc) · 5.44 KB
/
server.js
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
import { serveTls } from "https://deno.land/[email protected]/http/server.ts";
import { debugprint } from "./debug.js";
import * as state_machine from "./state_machine.js";
// For debugging
const staticFilesFlag = false;
const websocketFlag = false;
const socketstateFlag = false;
// ----------------------------------------------------------------------------
// State
// ----------------------------------------------------------------------------
export var sockets = new Map(); //contains objects with socket and state of socket
let games = new Map(); //contains game objects
// ----------------------------------------------------------------------------
// Server request handler, handles requests to the server
// ----------------------------------------------------------------------------
async function reqHandler(request) {
if (request.headers.get("upgrade") != "websocket") {
//Client is requesting static files
let resourceFromPath = new Map([
[ /^\/$/, "./public/index.html"], //detects root, a single "/"
[ /^\/favicon.ico$/, "./public/favicon.ico"], // Detects favicon.ico
[ /^\/favicon.svg/, "./public/favicon.svg"], // Detects favicon.svg
[ /^\/styles\/[\w\/-]+\.css$/, "PATH"], //matches css files in /styles/
[ /^\/scripts\/[\w\/-]+\.js$/, "PATH"], //matches js files in /scripts/
[ /^\/fonts\/[\w\/-]+\.woff2$/, "PATH"],//matches font files
[ /^\/graphics\/[\w\/-]+\.svg$/, "PATH"], //matches svg files
]);
let mimeTypeFromExtension = new Map([
[".html", { "content-type": "text/html; charset=utf-8" }],
[".css", { "content-type": "text/css" }],
[".js", { "content-type": "application/javascript" }],
[".woff2", { "content-type": "application/x-font-woff2" }],
[".svg", { "content-type": "image/svg+xml" }],
[".ico", { "content-type": "image/x-icon" }],
]);
const { pathname: path } = new URL(request.url);
debugprint("Path: "+path, staticFilesFlag);
for (const key of resourceFromPath.keys()) {
if (key.test(path)) {
let resource = resourceFromPath.get(key);
resource = resource === "PATH" ? "./public/"+path : resource;
let fileExtension = resource.match(/\.\w+$/)[0];
const body = await Deno.readFile(resource);
return new Response(body, {
status: 200,
headers: mimeTypeFromExtension.get(fileExtension)
});
break;
}
}
return new Response(null, { status: 404 }); //invalid path
}
//upgrade websocket requested
var websocketDetails = Deno.upgradeWebSocket(request);
var response = websocketDetails.response;
var websocket = websocketDetails.socket;
//add websocket to list of websockets
const identifier = crypto.randomUUID()
sockets.set(identifier, {socket: websocket, state: "NameSelection"});
debugprint("New websocket connection", websocketFlag);
debugprint("Websocket given identifier " + identifier, websocketFlag);
debugprint(sockets, websocketFlag);
//dealing with messages from websocket
websocket.onclose = () => {
if (sockets.get(identifier).state === "InGame")
state_machine.exitGame(identifier, JSON.stringify({command: "EXIT"}), sockets, games); // no zombies if disconnections
sockets.delete(identifier);
debugprint("Websocket " + identifier + " closed.", websocketFlag);
}
websocket.onmessage = (message) => {
debugprint("Message received from socket " + identifier, websocketFlag);
debugprint(message.data, websocketFlag);
// Use state machine to evaluate message appropriately
let socketState = state_machine.STATES.get(sockets.get(identifier).state);
debugprint("Socket's current state", websocketFlag);
debugprint(socketState, socketstateFlag);
let noMethodCalled = true;
socketState.methods.forEach((obj) => {
if (obj.validator(message.data)) {
debugprint("Validated message: " + message.data, socketstateFlag);
debugprint("Calling method based on message...", socketstateFlag);
obj.method(identifier, message.data, sockets, games);
noMethodCalled = false;
sockets.get(identifier).state = obj.nextState;
debugprint("State changing to " + obj.nextState + "...", socketstateFlag);
debugprint("State changed to " + sockets.get(identifier).state, socketstateFlag);
}
});
if (socketState.methods.length === 0 || noMethodCalled)
socketState.error(identifier);
}
websocket.onopen = () => {
//send to socket initial info about what game exist
state_machine.updateClientsGames(games, [identifier]);
}
return response;
}
// ----------------------------------------------------------------------------
//Sending messages to clients
// ----------------------------------------------------------------------------
export var sendToClients = (clientIDs, message) => {
if (clientIDs === "everyone") {
clientIDs = []
sockets.forEach((socketobj, uid) => {clientIDs.push(uid);});
}
clientIDs.forEach((uid) => {
try {sockets.get(uid).socket.send(message)}
catch(e) {debugprint("Tried to send a message to closed socket.", websocketFlag);}}); //TODO fix this eventually I guess
}
// ----------------------------------------------------------------------------
//Start server
// ----------------------------------------------------------------------------
serveTls(reqHandler, {
port: 8002,
certFile: "./path/to/cert.pem",
keyFile: "./path/to/privkey.pem",
});