DegenesisReference.com

CI/CD & Tooling Frontend
DegenesisReference.com

This was a fun project born purely out of my own personal frustration (the best kind of project).

Technical Specification

A Node.js script that extracts structured data from a source PDF and emits JSON for application consumption. Data served by a simple React application and hosted as a lightweight, static page.

The History

Degenesis is a tabletop role-playing game in the same vein as the quite-popular Dungeons & Dragons, but it’s set in a “primal punk” post-apocalyptic setting, instead of the ubiquitous “high-fantasy” of D&D and others. My friends and I had started playing D&D after we finished college in 2016 as a way to stay in touch and socialize using a virtual tabletop system called Roll20. One of the wonderful things about the D&D ecosystem is the plethora of both third-party and officially sanctioned digital tools available to new and experienced players. This encompasses everything from digital player handbooks to character creation walkthroughs, and even entire digital hubs that can hold a group’s players, story, notes, and any relevant gameplay assets.

Degenesis had none of this.

It’s a wonderful, challenging game with incredible artwork and a deeply palpable world that you can feel. This makes sense, because the game was created by artists and came out of inspiration from their artistic pursuits. As a result, the books for the world and for player creation are lore-rich – and rightfully so. Much as I enjoyed this, I became frustrated as we started trying to play the game. Okay, I have this item, what does it do? Gotta page through hundreds of pages… wait, the actual table I need is over there? Okay…

So, I got busy.

The Project

All of the data for the game was fit into dozens of un-standardized tables of varying format spread throughout a large PDF “player handbook”. This was frustrating to sort through manually, even performing a PDF search on a computer would take considerable time to search and then return a multitude of unwanted hits. To solve this problem, I wrote a script (node, naturally...) to search the PDF, look for tables, and parse the data out into a JSON file. Because of the scope of this project (my friends and I) and the immutability of data, I determined a full database would be overkill and I could just use the JSON file itself as the datasource. If I really needed to change something that could be done manually since this project didn’t need CRUD. Data, done.

To display this data, I continued in my line of pragmatic minimalism – I would render a static page with an extremely lightweight React component hierarchy. Since the tables in the book were different and there was zero standardization of columns or content taxonomy, I would simply render each table independent of the others based on what it contained. This was a pattern that was naturally compatible with React - I could simply get the data and have React render each table as a component, each line as a component, etc. Content, done (but not practical).

The data was barely more useful that the PDF if it couldn’t be searchable and sortable. I figured I could classify the data based on the tables I was displaying and tie each table to a checkbox. Fortunately, disparate as the data was in the PDF, things were still sorted by “type”, so I could mostly keep that. So a table of “camping equipment” became a checkbox for the “camping equipment” table, this was a very simply way for a user to quickly trim down what they were seeing. To multiply the usefulness of the search, I added a simple text-search function, capitalizing on React's active user interface. I could update the search string on each stroke and use methods in the state wrapper component to hoist state up from the children (searchable items) so that every keystroke limited the searchable results to any matching substring. This results in an instantaneous searched response since all the searchable items are effectively cached with the JSON datasource. Content, practical.

Last thing in line was to get it hosted, not a big feat for what is basically a static site. I initially hosted this on a Raspberry Pi and use PM2 to manage the process, a basic Express web server with a route or two, but later moved to a GitHub pages instance for ease and security.

Overall, this was a simple project and I was able to put it together quickly. Furthermore, it legitimately improved the experience for myself and my friends as we played the game. I call it a success.