collider.jam
Version:
Hand-crafted and fun to use game framework for jamming, creative coding, and rapid game development
259 lines (214 loc) • 7.93 kB
JavaScript
const express = require('express')
const url = require('url')
const env = require('./env')
const log = require('./log')
const scanner = require('./scanner')
const packager = require('./packager')
const flow = require('./flow')
const lib = require('./lib')
const meta = require('./meta')
const control = require('./mc/control')
const war = require('./war')
const TAG = 'hub'
function start() {
const app = express()
const ws = require('express-ws')(app);
app.use(express.json({ limit: '4mb' }))
log.out('=== COLLIDER.JAM ===')
log.out('version: ' + env.version)
log.out('====================')
log.debug('starting collider.jam hub...', TAG)
// fix module paths
module.paths.push('./')
module.paths.push('./node_modules')
const unitsMap = scanner.scan()
// TODO figure out do we really need that in some scenarios?
if (env.config.cors) {
log.debug('Allowing cross-origin access')
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-type, Accept')
next()
})
}
if (env.dynamic) {
env.config.dynamic = true
// mount units
let rootFlag = false
unitsMap.forEach(u => {
let unitURL = lib.addPath(env.base, u.id)
log.debug('mounting ' + unitURL + ' -> ' + u.path, TAG)
if (unitURL === '/') rootFlag = true
app.use(unitURL, express.static(u.path))
u.url = unitURL
})
if (!rootFlag) {
log.error('===========================================================', TAG)
log.error('No root path mounted! Could be a trouble with units.config!', TAG)
log.error('===========================================================', TAG)
}
app.get(env.base + env.configPath, function(req, res) {
res.json(env.config)
})
app.get('*/' + env.unitsPath, function(req, res) {
let unitId = req.path.substring(0, req.path.length - env.unitsPath.length - 1)
unitId = unitId.substring(env.base.length)
log.debug('building map for unit [' + unitId + ']', TAG)
if (unitId === '' || unitId === '/') {
//let units = scanner.scan(env.baseDir, env.scanMap)
let units = scanner.scan()
res.json(units.map)
} else {
// find unit's real path before the scan
// TODO are we looking for mixes or units or both here?
let unit = unitsMap.units[unitId]
if (unit) {
let map = scanner.rescanUnit(unit)
res.json(map)
} else {
res.status(404).send('unit [' + unitId + '] not found!')
}
}
})
app.get('/units.debug', function(req, res) {
log.out('units debug')
//let map = scanner.scan(env.baseDir, env.scanMap)
let map = scanner.scan()
res.json({
units: map.units,
mix: map.mix,
})
})
app.post('/help/sync', function(req, res) {
// handle help data
log.debug('receiving help data from the client...')
//console.dir(req.body)
meta.sync(req.body)
res.status(200).send('done')
})
app.get('/help/data', function(req, res) {
if (env.cache.help) {
res.json(env.cache.help)
} else {
res.status(404).send('No help data!')
}
})
app.get('/help/defs', function(req, res) {
if (meta.map) {
res.json(meta.map)
} else {
res.status(404).send('No definition data!')
}
})
app.get('/help/card', function(req, res) {
if (env.cache.help) {
res.send(env.cache.help.pages['jam-card'].body)
} else {
res.status(404).send('No help data!')
}
})
/*
* let typeMap = {
'fi': vscode.CompletionItemKind.Field,
'v': vscode.CompletionItemKind.Variable,
'f': vscode.CompletionItemKind.Function,
'p': vscode.CompletionItemKind.Property
};
*/
app.get('/help/autocomplete', function(req, res) {
if (env.cache.help) {
const query = url.parse(req.url,true).query
res.send(meta.autocomplete(
query.context, query.file))
} else {
res.status(404).send('No help data!')
}
})
app.get('/help/definition', function(req, res) {
if (env.cache.help) {
const query = url.parse(req.url,true).query
res.send( meta.definition(
query.context, query.file))
} else {
res.status(404).send('No help data!')
}
})
if (env.config.war) {
app.get(env.base + env.warPath, function(req, res) {
res.json(env.war || {})
})
}
// try to load cached meta
meta.load()
} else {
log.out('serving only static package!', TAG)
env.config.dynamic = false
env.flow = false
env.config.flow = false
if (!env.pregen) {
packager.pack(env.baseDir, env.outDir, unitsMap)
} else {
env.outDir = packager.determineFinalOutputDir()
log.out(`using pregenerated package [${env.outDir}]`, TAG)
}
let localPath = lib.addPath(env.baseDir, env.outDir)
log.debug(`mounting [${env.base}] -> [${localPath}]`, TAG)
app.use(env.base, express.static(localPath)) // mount to root
}
// try to launch mission control
if (env.mc) {
control.start(this, app)
}
// try to boost
// TODO check fs on boost availability first
if (env.hub) {
const boost = require('hub/boost')
log.debug('boosting the app', TAG)
boost(app, env)
}
process.on('uncaughtException', (err) => {
if (env.debug) {
log.dump(err)
} else {
log.error(err)
}
// provide details and hints
if (err.toString().includes('address already in use')) {
log.error('looks like the port ' + env.port + ' is occupied by another app')
log.error('check that collider.jam is not running in another console')
}
})
app.listen(env.port, () => {
log.out('===========================================')
log.out('--- Play at http://localhost:' + env.port + ' ---', TAG)
log.out('===========================================')
})
if (env.flow) {
flow.start(app, ws)
startSyncMonitor()
}
if (env.config.war) startAirRaidAlertMonitor()
}
function startSyncMonitor() {
//if (!env.config.dynamic && !env.config.debug && !env.config.flow) return
// TODO should be configurable (?)
const syncTime = 2000
const syncTimeS = Math.round(syncTime/1000)
log.debug(`running file sync every ${syncTimeS}s...`, TAG)
setInterval(() => {
scanner.sync()
}, syncTime)
}
function startAirRaidAlertMonitor() {
//if (!env.config.dynamic && !env.config.debug && !env.config.flow) return
const checkAlertPeriod = 5000,
checkAlertPeriodS = Math.round(checkAlertPeriod/1000)
log.debug(`checking for air raid alerts every ${checkAlertPeriodS}s...`, TAG)
setInterval(() => {
war.checkAlerts()
}, checkAlertPeriod)
}
module.exports = {
start: start,
}