// Copyright Epic Games, Inc. All Rights Reserved. var httpPort = 90; var matchmakerPort = 9999; var enableRedirectionLinks = true; var enableRESTAPI = true; var enableLogToFile = true; const argv = require('yargs').argv; const express = require('express'); var cors = require('cors') const app = express(); const http = require('http').Server(app); const logging = require('./modules/logging.js'); logging.RegisterConsoleLogger(); if (enableLogToFile) { logging.RegisterFileLogger('./logs'); } // A list of all the Cirrus server which are connected to the Matchmaker. var cirrusServers = new Map(); // // Parse command line. // if (typeof argv.httpPort != 'undefined') { httpPort = argv.httpPort; } if (typeof argv.matchmakerPort != 'undefined') { matchmakerPort = argv.matchmakerPort; } // // Connect to browser. // http.listen(httpPort, () => { console.log('HTTP listening on *:' + httpPort); }); // Get a Cirrus server if there is one available which has no clients connected. function getAvailableCirrusServer() { for (cirrusServer of cirrusServers.values()) { if (cirrusServer.numConnectedClients === 0 && cirrusServer.ready === true) { return cirrusServer; } } console.log('WARNING: No empty Cirrus servers are available'); return undefined; } // No servers are available so send some simple JavaScript to the client to make // it retry after a short period of time. function sendRetryResponse(res) { res.send(`All ${cirrusServers.size} Cirrus servers are in use. Retrying in 10 seconds. `); } if(enableRESTAPI) { // Handle REST signalling server only request. app.options('/signallingserver', cors()) app.get('/signallingserver', cors(), (req, res) => { cirrusServer = getAvailableCirrusServer(); if (cirrusServer != undefined) { res.json({ signallingServer: `${cirrusServer.address}:${cirrusServer.port}`}); console.log(`Returning ${cirrusServer.address}:${cirrusServer.port}`); } else { res.json({ signallingServer: '', error: 'No signalling servers available'}); } }); } if(enableRedirectionLinks) { // Handle standard URL. app.get('/', (req, res) => { cirrusServer = getAvailableCirrusServer(); if (cirrusServer != undefined) { res.redirect(`http://${cirrusServer.address}:${cirrusServer.port}/`); console.log(req); console.log(`Redirect to ${cirrusServer.address}:${cirrusServer.port}`); } else { sendRetryResponse(res); } }); // Handle URL with custom HTML. app.get('/custom_html/:htmlFilename', (req, res) => { cirrusServer = getAvailableCirrusServer(); if (cirrusServer != undefined) { res.redirect(`http://${cirrusServer.address}:${cirrusServer.port}/custom_html/${req.params.htmlFilename}`); console.log(`Redirect to ${cirrusServer.address}:${cirrusServer.port}`); } else { sendRetryResponse(res); } }); } // // Connection to Cirrus. // const net = require('net'); function disconnect(connection) { console.log(`Ending connection to remote address ${connection.remoteAddress}`); connection.end(); } const matchmaker = net.createServer((connection) => { connection.on('data', (data) => { try { message = JSON.parse(data); } catch(e) { console.log(`ERROR (${e.toString()}): Failed to parse Cirrus information from data: ${data.toString()}`); disconnect(connection); return; } if (message.type === 'connect') { // A Cirrus server connects to this Matchmaker server. cirrusServer = { address: message.address, port: message.port, numConnectedClients: 0 }; cirrusServer.ready = message.ready === true; cirrusServers.set(connection, cirrusServer); console.log(`Cirrus server ${cirrusServer.address}:${cirrusServer.port} connected to Matchmaker, ready: ${cirrusServer.ready}`); } else if (message.type === 'streamerConnected') { // The stream connects to a Cirrus server and so is ready to be used cirrusServer = cirrusServers.get(connection); if(cirrusServer) { cirrusServer.ready = true; console.log(`Cirrus server ${cirrusServer.address}:${cirrusServer.port} ready for use`); } else { disconnect(connection); } } else if (message.type === 'streamerDisconnected') { // The stream connects to a Cirrus server and so is ready to be used cirrusServer = cirrusServers.get(connection); if(cirrusServer) { cirrusServer.ready = false; console.log(`Cirrus server ${cirrusServer.address}:${cirrusServer.port} no longer ready for use`); } else { disconnect(connection); } } else if (message.type === 'clientConnected') { // A client connects to a Cirrus server. cirrusServer = cirrusServers.get(connection); if(cirrusServer) { cirrusServer.numConnectedClients++; console.log(`Client connected to Cirrus server ${cirrusServer.address}:${cirrusServer.port}`); } else { disconnect(connection); } } else if (message.type === 'clientDisconnected') { // A client disconnects from a Cirrus server. cirrusServer = cirrusServers.get(connection); if(cirrusServer) { cirrusServer.numConnectedClients--; console.log(`Client disconnected from Cirrus server ${cirrusServer.address}:${cirrusServer.port}`); } else { disconnect(connection); } } else { console.log('ERROR: Unknown data: ' + JSON.stringify(message)); disconnect(connection); } }); // A Cirrus server disconnects from this Matchmaker server. connection.on('error', () => { cirrusServer = cirrusServers.get(connection); cirrusServers.delete(connection); if(cirrusServer) { console.log(`Cirrus server ${cirrusServer.address}:${cirrusServer.port} disconnected from Matchmaker`); } else { console.log(`Disconnected machine that wasn't a registered cirrus server, remote address: ${connection.remoteAddress}`); } }); }); matchmaker.listen(matchmakerPort, () => { console.log('Matchmaker listening on *:' + matchmakerPort); });