Skip to content

Commit

Permalink
replace koa with hapi
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffomatic committed Jan 3, 2021
1 parent e5a40b5 commit 0ff2e2d
Show file tree
Hide file tree
Showing 8 changed files with 653 additions and 773 deletions.
10 changes: 4 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@
"perf_hooks": "./src/browserShims/perf_hooks.ts"
},
"dependencies": {
"@hapi/hapi": "^20.0.3",
"@hapi/inert": "^6.0.3",
"file-saver": "^2.0.2",
"gl-matrix": "^3.3.0",
"koa": "^2.13.0",
"koa-router": "^9.4.0",
"koa-send": "^5.0.1",
"lodash": "^4.17.19",
"react": ">= 16.8.0",
"react-dom": ">= 16.8.0",
Expand All @@ -37,10 +36,9 @@
"@types/events": "^3.0.0",
"@types/file-saver": "^2.0.1",
"@types/gl-matrix": "^2.4.5",
"@types/hapi__hapi": "^20.0.3",
"@types/hapi__inert": "^5.2.2",
"@types/jest": "^25.2.3",
"@types/koa": "^2.11.4",
"@types/koa-router": "^7.4.1",
"@types/koa-send": "^4.1.2",
"@types/lodash": "^4.14.152",
"@types/react": "^16.9.35",
"@types/react-dom": "^16.9.8",
Expand Down
4 changes: 0 additions & 4 deletions src/cli/build/buildServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ fs.mkdirSync(path.normalize(path.join(buildkeyPath, '..')), { recursive: true })
fs.writeFileSync(buildkeyPath, buildkey)
console.log(`buildkey '${buildkey}' written to ${buildkeyPath}`)

// NOTE: this will emit a somewhat silly error due to a transitive dependency
// issue deep within koa. koa 2 uses koa-convert, which declares an outdated
// version of koa-compose, which in turn imports any-promise, which uses a
// require() statement with a non-static argument, hence the warning.
esbuild.buildSync({
bundle: true,
entryPoints: [path.join(gameSrcPath, 'server', 'main.ts')],
Expand Down
2 changes: 1 addition & 1 deletion src/client/autoReload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const poll = (initialBuildkey: string): void => {
return
}

window.autoReload.interval = setInterval(() => {
window.autoReload.interval = window.setInterval(() => {
fetch('/api/buildkey')
.then((response) => {
if (response.status != 200) {
Expand Down
2 changes: 1 addition & 1 deletion src/client/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ document.addEventListener('keyup', (event) => {
return
}

restartHotkeyTimeout = setTimeout(() => {
restartHotkeyTimeout = window.setTimeout(() => {
restartHotkeyTimeout = undefined
}, 500)

Expand Down
178 changes: 112 additions & 66 deletions src/server/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import * as fs from 'fs'
import * as path from 'path'

import Koa from 'koa'
import KoaRouter from 'koa-router'
import koaSend from 'koa-send'
import * as hapi from '@hapi/hapi'
import inert from '@hapi/inert'
import * as WebSocket from 'ws'

import { buildkeyPath, clientBuildOutputPath } from '~/cli/build/common'
Expand All @@ -12,78 +11,125 @@ import { SIMULATION_PERIOD_S } from '~/constants'
import { ClientConnectionWs } from '~/network/ClientConnection'
import { ServerSim } from '~/server/ServerSim'

// TODO: read from envvar
const playerCount = 1
const clientBufferSize = 7
async function buildVersion(): Promise<string> {
return (await fs.promises.readFile(buildkeyPath)).toString()
}

let gameSim = new ServerSim({
playerCount,
minFramesBehindClient: clientBufferSize,
})
async function entrypointTemplate(): Promise<string> {
return (
await fs.promises.readFile(path.join(clientBuildOutputPath, 'index.html'))
).toString('utf8')
}

setInterval(
() => gameSim.update(SIMULATION_PERIOD_S),
(1000 * SIMULATION_PERIOD_S) / 2,
)

const httpServer = new Koa()
const port = 3000

const buildkey = fs.readFileSync(buildkeyPath).toString()
const entrypointPage = fs
.readFileSync(path.join(clientBuildOutputPath, 'index.html'))
.toString('utf8')
const entrypointWithAutoReload = updateEntrypointHtmlForAutoReload({
buildkey,
html: entrypointPage,
})
async function main(): Promise<void> {
// TODO: read from envvar
const playerCount = 1
const clientBufferSize = 7
const port = 3000

const wsServer = new WebSocket.Server({ noServer: true })
const apiRouter = new KoaRouter()
apiRouter
.get('/buildkey', async (ctx) => (ctx.body = buildkey))
.get('/restart', async (ctx) => {
console.log('restarting game server')
gameSim.shutdown()
gameSim = new ServerSim({
playerCount,
minFramesBehindClient: clientBufferSize,
})
ctx.body = 'ok'
let gameSim = new ServerSim({
playerCount,
minFramesBehindClient: clientBufferSize,
})
.get('/connect', async (ctx) => {
if (ctx.get('Upgrade') !== 'websocket') {
ctx.throw(400, 'invalid websocket connection request')
}

ctx.respond = false
setInterval(
() => gameSim.update(SIMULATION_PERIOD_S),
(1000 * SIMULATION_PERIOD_S) / 2,
)

const socket = await new Promise((resolve: (ws: WebSocket) => void) => {
wsServer.handleUpgrade(ctx.req, ctx.req.socket, Buffer.alloc(0), resolve)
})
const wsServer = new WebSocket.Server({ noServer: true })
const httpServer = new hapi.Server({
port,
host: 'localhost',
})

console.log(
`websocket connection established with ${ctx.req.socket.remoteAddress}`,
)
await httpServer.register(inert)

gameSim.connectClient(new ClientConnectionWs(socket))
httpServer.route({
method: 'GET',
path: '/',
handler: async () => {
const [bv, template] = await Promise.all([
buildVersion(),
entrypointTemplate(),
])
return updateEntrypointHtmlForAutoReload({ buildkey: bv, html: template })
},
})

const router = new KoaRouter()
router.use('/api', apiRouter.routes())
router
.get('/', async (ctx, next) => {
ctx.body = entrypointWithAutoReload
return await next()
httpServer.route({
method: 'GET',
path: '/api/buildkey',
handler: async () => {
return await buildVersion()
},
})
// FIXME: this route catches requests with the `/api` prefix that apiRouter
// doesn't handle. This is suprising and weird.
.get('/(.*)', async (ctx) =>
koaSend(ctx, ctx.path, {
root: clientBuildOutputPath,
}),
)

console.log(`Starting dev server on port ${port}, buildkey ${buildkey}`)
httpServer.use(router.routes()).use(router.allowedMethods())
httpServer.listen(port)
httpServer.route({
method: 'GET',
path: '/api/restart',
handler: () => {
console.log('restarting game server')
gameSim.shutdown()
gameSim = new ServerSim({
playerCount,
minFramesBehindClient: clientBufferSize,
})
return ''
},
})

httpServer.route({
method: 'GET',
path: '/api/connect',
handler: async (request, h) => {
if (request.headers['upgrade'] !== 'websocket') {
return h
.response({
error: 'invalid websocket connection request',
})
.code(400)
}

const nodeReq = request.raw.req
const tcpSocket = nodeReq.socket
const webSocket = await new Promise(
(resolve: (ws: WebSocket) => void) => {
wsServer.handleUpgrade(nodeReq, tcpSocket, Buffer.alloc(0), resolve)
},
)

console.log(
`websocket connection established with ${tcpSocket.remoteAddress}`,
)

gameSim.connectClient(new ClientConnectionWs(webSocket))

// The WebSocket server now controls the socket, so let Hapi stop worrying
// about it.
return h.abandon
},
})

httpServer.route({
method: 'GET',
path: '/{param*}',
handler: {
directory: {
path: clientBuildOutputPath,
redirectToSlash: true,
},
},
})

const bv = await buildVersion()
console.log(`Starting dev server on port ${port}, build version ${bv}`)
await httpServer.start()
}

main()

process.on('unhandledRejection', (err) => {
console.log(err)
process.exit(1)
})
2 changes: 1 addition & 1 deletion src/tools/map/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const Controls = ({ editor }: { editor: Editor }): ReactElement => {
let savingTimeout: number | null = null
editor.events.addListener('changed', () => {
if (savingTimeout === null) {
savingTimeout = setTimeout(() => {
savingTimeout = window.setTimeout(() => {
savingTimeout = null
saveMap(editor.map)
console.log('saved state')
Expand Down
4 changes: 2 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dom"
],
"moduleResolution": "node",
"allowSyntheticDefaultImports": true, // required for using Koa
"allowSyntheticDefaultImports": true, // required for modules with default exports
"alwaysStrict": true,
"noImplicitAny": true,
"noImplicitThis": true,
Expand Down Expand Up @@ -34,4 +34,4 @@
"out",
"node_modules"
]
}
}
Loading

0 comments on commit 0ff2e2d

Please sign in to comment.