/**
* @license
* Copyright 2018-2020 MobiledgeX, Inc. All rights and licenses reserved.
* MobiledgeX, Inc. 156 2nd Street #408, San Francisco, CA 94105
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileOverview server.js is where the server logic resides, where the websocket and udp servers are being instantiated
* @module server
* @requires http
* @requires WebSocket
* @requires url
* @requires dgram
*
*/
const http = require('http')
const WebSocket = require('ws')
const url = require('url')
const server = http.createServer()
const wsServer = new WebSocket.Server({ noServer: true })
const dgram = require('dgram')
const udpServer = dgram.createSocket('udp4')
const Lobby = require('./models/Lobby').Lobby
const UDPClient = require('./models/UDPClient').UDPClient
const events = require('./models/Events').Events
const util = require('./util/util').util
const MAX_ROOMS_PER_LOBBY = 5 // Change to the desired maximum rooms per lobby based on your number of rooms members per room & the app instance flavor
let lobby = new Lobby(MAX_ROOMS_PER_LOBBY)
server.on('upgrade', function upgrade(request, socket, head) {
var parsedUrl = url.parse(request.url, true, true)
const pathName = parsedUrl.pathname
if (pathName === '/') {
wsServer.handleUpgrade(request, socket, head, function done(ws) {
wsServer.emit('connection', ws, request)
})
} else {
socket.destroy()
}
})
wsServer.on('connection', function connection(ws, request) {
console.log('num of rooms in the Lobby : ' + lobby.rooms.length)
console.log('player Connected')
let playerKey = request.headers['sec-websocket-key']
let playerId = lobby.addPlayer(playerKey, ws)
let roomId = ''
console.log(`sending registerEvent to client`)
ws.send(new events.RegisterEvent(playerId, playerKey).convertToJSONString())
ws.on('message', function (msgStr) {
jsonObj = JSON.parse(msgStr)
switch (jsonObj.type) {
case 'JoinOrCreateRoom':
console.log('JoinOrCreateRoom Request received from client')
roomId = util.joinOrCreateRoom(lobby, jsonObj.playerId, jsonObj.playerName, jsonObj.playerAvatar, jsonObj.maxPlayersPerRoom)
break
case 'GetRooms':
console.log('GetRooms Request received from client')
var connection = lobby.getPlayerConnection(playerId)
connection.send(new events.RoomsListEvent(lobby.rooms).convertToJSONString())
break
case 'GetAvailableRooms':
console.log('GetAvailableRooms Request received from client')
var connection = lobby.getPlayerConnection(playerId)
connection.send(new events.AvailableRoomsListEvent(lobby.availableRooms).convertToJSONString())
break
case 'CreateRoom':
console.log('CreateRoom Request received from client %o', jsonObj)
roomId = util.createRoom(lobby, jsonObj.playerId, jsonObj.playerName, jsonObj.playerAvatar, jsonObj.maxPlayersPerRoom)
break
case 'JoinRoom':
console.log('JoinRoom Request received from client %o', jsonObj)
roomId = util.joinRoom(lobby, jsonObj.roomId, jsonObj.playerId, jsonObj.playerName, jsonObj.playerAvatar)
break
case 'ExitRoom':
console.log('ExitRoom Request received from client %o', jsonObj)
roomId = util.exitRoom(lobby, jsonObj.roomId, jsonObj.playerId)
break
case 'GamePlayEvent':
var room = lobby.getRoomById(jsonObj.roomId)
if (room !== undefined) {
room.broadcastGamePlayEvent(lobby, jsonObj)
}
break
default:
console.log('Unknown msg Received from client with type ' + jsonObj.type)
break
}
})
ws.on('error', (err) => {
console.log(`WebSocket Server error : ${err}`)
})
ws.on('close', function (code) {
console.log(`client closed ,close code: ${code}`)
console.log(`member left, member uuid : ${playerId}, roomId : ${roomId}`)
// remove member from room
var room = lobby.getRoomById(roomId)
if (room === undefined) {
return
}
room.removePlayer(playerId)
// remove player connection
lobby.removePlayer(playerId)
// delete room if empty
if (room.isEmpty()) {
lobby.removeRoom(roomId)
return
}
// if room is not empty, notify other roomMembers that a player left
room.broadcastGameFlowEvent(lobby, new events.RoomMemberLeftEvent(playerId))
return
})
})
udpServer.on('error', (err) => {
console.log(`UDP server error:\n${err.stack}`)
udpServer.close()
})
udpServer.on('message', (gameplayEventBinary, senderInfo) => {
udpClient = new UDPClient(senderInfo.address, senderInfo.port)
var gameplayEventJSON = JSON.parse(gameplayEventBinary)
if (!lobby.udpConnectionExists(udpClient)) {
lobby.udpConnections[JSON.stringify(udpClient)] = true
lobby.updateRoomUDPClientsMap(gameplayEventJSON.roomId, udpClient)
}
var gameplayEventBuffer = new Buffer.from(gameplayEventBinary)
udpClients = lobby.udpConnectionsPerRoom[gameplayEvent.roomId]
udpClients.forEach(udpClient => {
udpServer.send(gameplayEventBuffer, 0, gameplayEventBuffer.length, udpClient.port, udpClient.address)
})
})
udpServer.on('listening', () => {
const udp_address = udpServer.address()
console.log(`UDP server listening on ${udp_address.address}:${udp_address.port}`)
})
udpServer.bind(5000)
server.listen(3000, () => {
const address = server.address()
console.log(`WebSocket is listening on ${address.address}:${address.port}`)
})