Skip to content

Instantly share code, notes, and snippets.

@timetocode
Created October 17, 2018 01:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timetocode/0bd1ca783fcb97457083e7091b26368e to your computer and use it in GitHub Desktop.
Save timetocode/0bd1ca783fcb97457083e7091b26368e to your computer and use it in GitHub Desktop.
Example of using tls-json + express to create a master server listing service. master.js uses tls-json to listen over tls and accept connections + listing data from other services that connect, meanwhile instance.js shows a snippet of a game server that is listing itself on the master server. server.json is an example of what the http call to th…
// from the game instance
const master = new TLSClient({
options: {
ca: fs.readFileSync(config.API_CERT),
},
reconnectInterval: 2000,
host: config.MASTER_API_HOST,
port: config.MASTER_API_PORT,
password: config.API_PASSWORD
})
master.on('authenticated', () => {
syncListing()
})
master.on('message', message => { ... })
master.on('close', () => { ... })
master.on('error', err => { ... })
function syncListing() {
master.send({
type: 'list',
listing: {
version: config.GAME_VERSION,
port: 8001,
currentPlayers: 0, // these would be variables
maxPlayers: 100,
mode: 'idle',
etc: 'anything'
}
})
}
function updatePlayerCount() {
master.send({
type: 'list',
listing: {
currentPlayers: 10
}
})
}
const fs = require('fs')
const express = require('express')
const config = require('config')
const TLSServer = require('tls-json').Server
const Router = require('express-promise-router')
const servers = require('../servers')
const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
// game instances connected to the master server
const clients = new Map()
let instanceListing = []
let invalidateCache = false
let cacheTimestamp = Date.now()
// rebuilds the public-facing server list
const rebuildListing = function () {
const now = Date.now()
// cache logic: don't rebuild the list unless it has been 500 ms
if (now > cacheTimestamp + 500 || invalidateCache) {
cacheTimestamp = now
invalidateCache = false
instanceListing = []
clients.forEach(client => {
const listing = {}
for (prop in client) {
if (prop !== 'socket') {
listing[prop] = client[prop]
}
}
instanceListing.push(listing)
})
}
}
const server = new TLSServer({
options: {
key: fs.readFileSync(config.API_KEY),
cert: fs.readFileSync(config.API_CERT)
},
password: config.API_PASSWORD
})
server.on('authenticated', (id, socket) => {
let ip = socket.remoteAddress
if (ip.startsWith("::ffff:")) {
ip = ip.slice(7)
}
const subdomain = servers.byIp(ip)
// create the initial listing, id, socket, ip, subdomain
clients.set(id, { id, socket, ip, subdomain })
})
server.on('close', id => {
invalidateCache = true
clients.delete(id)
})
server.on('error', (id, err) => {
console.log(`client(${id})::error::${err}`)
})
server.on('message', (id, message) => {
if (message.type === 'list') {
// copy the submitted listing to our records
Object.assign(clients.get(id), message.listing)
}
})
server.on('request', (id, req, res) => { })
server.listen(config.MASTER_API_PORT, () => {
console.log(`MASTER API listening on ${config.MASTER_API_PORT}`)
})
const router = new Router()
// public server listing
router.get('/servers', async (req, res) => {
rebuildListing()
res.send(instanceListing)
})
app.use(router)
app.listen(7999, () => {
console.log(`MASTER HTTP listening on 7999`)
})
[
{"id":0,"ip":"127.0.0.1","version":"0.0.3","mode":"idle","port":8001,"currentPlayers":0,"maxPlayers":0},
{"id":1,"ip":"127.0.0.1","version":"0.0.3","mode":"idle","port":8002,"currentPlayers":0,"maxPlayers":0}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment