-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathserver.js
More file actions
209 lines (170 loc) · 7.54 KB
/
Copy pathserver.js
File metadata and controls
209 lines (170 loc) · 7.54 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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/**
* server.js
*
* Core server component that handles all realtime data transfer and static site hosting
*
* Created by Sameid Usmani on 08-05-17.
*/
// Simple constant files for identifying messages and errors
var MESSAGES = require("./public/js/messages");
var ERRORS = require("./public/js/errors");
// Libraries used to setup the static server and WebSocket server
var http = require('http');
var sockjs = require('sockjs');
var express = require("express");
var bodyParser = require("body-parser");
var path = require("path");
// Create an express app
var app = express();
// Add necessary middleware for encoding the URL and hosting the app from the public folder
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'public')));
// Either set the port to an ENVIRONMENT_VARIABLE or 3000
var port = process.env.PORT || 3000;
// Create a new router for hosting the game
var router = express.Router();
// Host the game entry point (index.html) on the '/' endpoint
router.get('/', function(req, res) {
res.status(200).sendFile(path.join(__dirname + '../public/index.html'));
});
// Use that router against the '/' endpoint
app.use('/', router);
// Listen for http request on the port specified
app.listen(port);
console.log("Shooty Roads hosting server started on", port);
// Create a json map for rooms where the key is the room name and the object holds the connections for both the client and server
var rooms = {};
// Create a json map for the connection id generated by SockJS and the value is the room name, this makes accessing the room via the connection id easy
var connectionRoomMap = {};
/**
* A helper function to send data to a SockJS connection
*
* @param conn {SockJSConnection}
* @param object {Object}
*/
var send = function(conn, object){
// Convert the object to a string, and send on the SockJSConnection
if (conn) {
conn.write(JSON.stringify(object));
}
}
/**
* Common pattern for relay messages to the opposite player in the room
*
* @param room {Room}
* @param message {Object}
*/
var relayMessage = function(room, message){
if (!room) {
return;
}
if (message.isHost) {
if (room.client) {
send(room.client, message);
}
} else {
if (room.server) {
send(room.server, message);
}
}
}
// Create a new SockJSServer
var webSocketServer = sockjs.createServer({ sockjs_url: 'http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js' });
// Listen to connection events on the SockJS server
webSocketServer.on('connection', function(conn) {
// Listen to the data events for any given connection
conn.on('data', function(message) {
// Since all messages for SockJS are sent via string, we need to parse the string into a JSON object
//XXX: This assumes all strings sent on the socket are in JSON format
var message = JSON.parse(message);
// Handle the event when the host is requesting to host a room
if (message.id == MESSAGES.HOSTING_GAME_REQUEST.id) {
// Check if the roomName provided doesn't already exist
if (!rooms[message.roomName]){
// If the room doesn't exist then create it
rooms[message.roomName] = {
server: conn, // Set the connection to the server key
serverName: message.serverName // Also append the serverName specified by the player
};
// Also add the roomName to the connection id mapping on the SockJSConnection object
connectionRoomMap[conn.id] = message.roomName;
console.log("Created room:", message.roomName);
// Send a message to the hoster, that creating the room was successful
send(conn, MESSAGES.SUCCESSFUL_ROOM_CREATION)
} else {
// Notify the host that the room already exists
send(conn, ERRORS.ROOM_ALREADY_EXISTS);
}
}
// Handle the event when the client is requesting to join a room
else if (message.id == MESSAGES.JOINING_GAME_REQUEST.id) {
// Check if the room exists, and if it's not already full
if (rooms[message.roomName] && !rooms[message.roomName].client) {
// If there is room in the room, then add the client to the room
var r = rooms[message.roomName];
r.client = conn;
// Notify the host of the room that someone has successfully joined the room
var serverMessage = MESSAGES.SUCCESSFUL_JOIN_RESPONSE;
serverMessage.clientName = message.clientName;
send(r.server, serverMessage);
// Notifu the client that they have successfully joined the room
var clientMessage = MESSAGES.SUCCESSFUL_JOIN_RESPONSE;
clientMessage.serverName = r.serverName
send(r.client, clientMessage);
// Create a mapping from the client connection id to the room name that they are in
connectionRoomMap[conn.id] = message.roomName;
} else {
// Notify the client that the room doesn't exist
//XXX: This will also send if the room is full
send(conn, ERRORS.ROOM_DOES_NOT_EXIST);
}
}
// Handle the event when the host has started the game
else if (message.id == MESSAGES.GAME_STARTED_BY_HOST.id) {
// Get the room, and notify the client that the game has started
var r = rooms[message.roomName];
send(r.client, MESSAGES.GAME_STARTED_BY_HOST);
}
// Handle all events for in game data
else if (message.id == MESSAGES.GAME_DATA.id) {
// Simply get the room and push the data to the appropriate person in the room
relayMessage(rooms[message.roomName], message);
}
// Handle the event if the host or client stopped the game (ie. they died)
else if (message.id == MESSAGES.GAME_OVER.id) {
// Notify the client or host directly
relayMessage(rooms[message.roomName], message);
}
// Handle the vevent if the host or client cancel the joining or hosting process
else if (message.id == MESSAGES.CANCEL.id) {
relayMessage(rooms[message.roomName], message);
}
});
// Listen to the close events on any given connection
conn.on('close', function() {
console.log("Client closed connection:", conn.id);
// Using the connectionRoomMap get the roomName and from that get the room object
var roomName = connectionRoomMap[conn.id] || null;
var r = roomName ? rooms[roomName] : null;
// If no room exists for the current connection, simply return
if (!r || !r.client || !r.server) {
return;
}
// Notify the host or client if either player has disconnected
if (r.client && r.client.id == conn.id) {
send(r.server, ERRORS.CLIENT_DISCONNECTED);
} else {
send(r.client, ERRORS.SERVER_DISCONNECTED);
}
// Expire the room afterwards
delete rooms[connectionRoomMap[conn.id]];
});
});
// Create an http server to host the SockJS server
var server = http.createServer();
// Install the handler to the webSocketServer, and listen on /multiplayer
webSocketServer.installHandlers(server, {prefix:'/multiplayer'});
// Start listening to SockJS request on 3001
server.listen(3001, '0.0.0.0');
console.log("SockJS server started on", 3001);