Project — "The Adventure Begins"
dnd-game-v2
A browser-based, D&D 5e-inspired dungeon crawler. Create a character, explore hand-built and procedurally-populated dungeons, and fight turn-based combat in real time over WebSockets — all served by a single BoxLang backend with no frontend build step.
Character creation & sheet
Species, class, background, alignment, standard-array ability scores, starting spells, fighting styles, multiclassing, inventory, and spell management.
Turn-based combat
Grid-based movement and line-of-sight, attacks, spellcasting, rage, smite, breath weapons, short/long rests, and death saves — synced live per encounter over WebSockets.
Map editor
Paint tile-based dungeon/outdoor maps, place spawn points, loot, and transitions between maps and modules. Desktop-only — grid painting doesn't translate to a phone.
Standing party & accounts
A persistent, server-side roster of up to 4 characters managed from a header toolbar; username/password accounts (PBKDF2-hashed) scope characters and maps to their owner.
Tech stack
| Layer | What |
|---|---|
| Backend | BoxLang (.bx classes, .bxm templates) running on CommandBox — no separate compile/build step for backend code. |
| Realtime | SocketBox (bundled BoxLang WebSocket module). |
| Database | MySQL. |
| Frontend | Vue 3 + Vue Router 4, loaded via CDN <script> tags — plain JS components, no bundler/npm build. |
| Styling | Hand-written CSS (public/assets/game.css), no framework. |
Project layout
models/ BoxLang service classes (business logic + SQL)
AuthService.bx Login/register, password hashing, ownership checks
CharacterService.bx Character creation, inventory, sheet data
CombatService.bx Facade over the combat engine — delegates to models/combat/
MapService.bx Map/tile storage, editor persistence, geometry decode
combat/ Combat engine, split into focused services:
DiceService.bx Dice notation parsing and rolling
RulesService.bx Ability/proficiency math, skills, species traits
GridService.bx Pathfinding, line of sight, walkability
MonsterLibrary.bx Stat-block parsing, rosters, spawn building
SpellcastingService.bx Caster classes, spell slots, spell lookups
ProgressionService.bx XP/level-ups, ASI, feats, multiclassing
CharacterStateService.bx Character load/save, short/long rests
CombatActionsService.bx Attack resolution, action economy, conditions
EnemyAIService.bx Enemy turns, aggro, legendary actions
public/
Application.bx App-level config (session, datasource, cache-busting)
WebSocket.bx Combat WebSocket message handler
index.bxm SPA shell (loads Vue + all component scripts)
api/ JSON endpoints consumed by the frontend (*.bxm)
assets/
app.js Router, root layout/nav, shared game state
components/*.js Vue components (one per screen)
game.css All styling
db/ Schema + reference-data dump
runtime/boxlang.json BoxLang engine config (datasource, caches, logging)
server.json CommandBox server config (webroot, WebSocket wiring)
Setup
Prerequisites
- CommandBox (installs BoxLang automatically via
server.json'scfenginesetting) - MySQL reachable at the host/port configured below
1. Database
Load the schema and reference data into a fresh gameserver database:
bashmysql -u root gameserver < db/schema.sql
mysql -u root gameserver < db/seed.sql
2. Configure the datasource (optional)
Defaults assume MySQL on 127.0.0.1:3306 with user root and no password, database gameserver. Override via environment variables before starting the server:
bashexport DB_HOST=127.0.0.1
export DB_PORT=3306
export DB_DATABASE=gameserver
export DB_USER=root
export DB_DRIVER=MySQL # or MSSQL, Postgres, etc.
3. Start the server
bashbox server start
First run installs the bx-mysql module automatically. The app is then served at the URL CommandBox prints (defaults to a random free port — pin one with box server start port=8080 if you want it fixed).
4. Create an account
Visit the app and register; there's no seeded admin user. The seeded modules (The Arena, Starter Dungeon) have no owner, so they're playable immediately but not editable via the map editor until claimed:
sqlUPDATE adventure_modules SET owner_user_id = <your-user-id> WHERE owner_user_id IS NULL;
If you're restoring an existing populated database rather than a fresh seed, the same applies to any characters rows with no owner.
Development notes
No frontend build step. Edit public/assets/**/*.js or game.css directly and reload — nothing to compile.
Cache-busting: every asset <script>/<link> tag in index.bxm carries a ?v= query string set once at server start (Application.bx). Restarting the server (box server restart) forces browsers to pick up frontend changes instead of serving stale cached JS/CSS — or, without a restart, load any page while logged in with ?resetassets=1 appended to regenerate the token immediately. Ignored for a logged-out visitor, so it can't be used to force cache invalidation for other users.
Formatting: box run-script format (or format:check for CI) formats models/ and root .bx files via boxlang format.
new WebSocket("/ws") resolves to wss:// automatically over HTTPS, no config changes needed. Just remember sessions are per-origin, so you'll need to log in again on the tunnel's URL even if you're already logged in on localhost.
Testing
All submissions must include corresponding test specs (tests/specs/) covering the change. Tests run via TestBox's HTTP runner (tests/runner.bxm), not a CommandBox CLI module. With the server running:
- Browser — visit
/tests/runner.bxmfor TestBox's visual HTML report. - CLI/CI —
curl -s "http://127.0.0.1:PORT/tests/runner.bxm?reporter=json"for a JSON summary (totalSpecs,totalPass,totalFail,totalError).
Useful query params: reporter (simple default, json, text, dot), bundles (restrict to one spec — needs the fully-qualified dotted path, e.g. ?bundles=tests.specs.unit.CombatServiceSpec), labels (restrict to labeled tests).
Credits
Dungeon/tile art from Kenney's Tiny Dungeon and Roguelike/RPG packs (CC0 1.0).