The next step in setting up our turn-based game is prepping up our server to do double-duty: it needs to both vend static HTML content and handle dynamic client-server traffic to run the game logic. To that end, we need to extend the initial configuration to support both.
Koa server
Boardgame.io integrates with the koa framework to serve HTML. It's a relatively straightforward web server that supports a static server (koa-static
); the server is even instrumented with the npm debug library and can give debug logging output if the DEBUG=koa-static
env var is set.
Armed with this knowledge, I can improve the server.js
file to vend static content.
const frontendJsPath = path.resolve(__dirname, '..');
const frontendPublicPath = path.resolve(__dirname, '../../public');
server.app.use(async (ctx, next) => {
console.log(`requesting ${ctx.request.path}`);
await next();
});
server.app.use(serve((frontendJsPath)));
server.app.use(serve((frontendPublicPath)));
Now we're making progress, but the client code doesn't work because the browser can't import npm modules. It's time to bundle up the client-side code into something a regular JS browser can handle.
Webpack
Being already familiar with webpack, I chose to grab that as my weapon of choice for generating browser-compatible code. First step is to get it into the project:
npm install --save-dev webpack
npm install --save-dev ts-loader
I set up the webpack config based on the guide and changed my build rule to tsc && webpack
. My output is now public/bundle.js
instead of build.
At this point, I realized I'd need two different configs for client and server code. I can do this with two tsconfig.json
files and the tsc -p
build option to select which one to build. With a little reorganizing of my code (client-specific in "client", server-specific in "server", and common in "game"), I can now generate two build artifacts: a server that can run in Node, and a client that the server vends to a browser that connects to it.
After uploading all that content (the build artifacts and the node_modules
directory) and giving it a run, I had a successful instance of the game running from my server.
Now to set up a lobby.
Supporting a lobby
Boardgame.io provides a rudimentary lobby server that makes it easy to create and connect to games (just replace the top-level game object in your code with a Lobby client). Not quite documented is that lobby also assumes that HTML components that are class hidden
will be hidden from user view (display: none
css rule works fine). At this point, I also discovered that the default config rules for caching on my Dreamhost server didn't work well; every piece of GET content was served with a Cache-Control: max-age=172800
. I added some cache-breaking to the .htaccess
file:
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires 0
Hiding colors from end-users
One last bit of housekeeping at this stage: the color database is a JSON object that gets read into memory, but it should live only in the server memory; clients should not get a whole copy of it. To that end, I stored the colors in private/colors.json
and used Node to fetch them (require('fs')
, require('path')
, dynamically load the file at boot time). I also had to get those files out of webpack's field of view so it didn't try (and fail) to bind fs
and path
into browser code. The following got added to webpack.config.js
:
resolve: {
fallback: {
'fs': false,
'path': false,
}
}
So in the common code, I can check if fs.readFileSync
is defined, and if it is not I know I'm in the browser and can avoid trying to load colors.
Give it a try!
The game is up and available for 2 or more players at http://swatch.fixermark.com; feel free to try it and let me know what you think!
Next post, I'll talk a bit about re-architecting the game to support different kinds of rounds.