An Age of Empires II: Definitive Edition companion — units, civilizations, buildings, technologies, the tech tree, computed counters, a battle simulator, unit comparison and patch-to-patch stat changes.
Next.js App Router · DuckDB over parquet · Tailwind · Radix · installable as a PWA.
bun install
bun run dev # http://localhost:3000
bun run test # bun test
bun run typecheck
bun run lint:fixEvery game value is generated from SiegeEngineers/aoe2techtree. Nothing under export/ is hand-written — regenerate it instead:
bun run sync-data # export/json, export/parquet, public/img
python3 scripts/diff-export.py # data/patch-changes.json, powers /changesRun them in that order: the diff compares the fresh export against the last committed one. sync-data needs the duckdb CLI and cwebp on PATH; pass --skip-images to skip the webp pass.
Every route sets its own title, description, canonical URL and social card, and the four entity routes prerender one static page per unit, civilization, building and technology. sitemap.xml and robots.txt are generated from the same data.
Canonicals and the sitemap need one absolute origin, and it has to be the same one on every host the deployment answers to. It comes from NEXT_PUBLIC_SITE_URL, falling back to Vercel's production hostname and then to https://towncenter.vercel.app — so previews and the project's other .vercel.app aliases point their canonicals at production rather than advertising themselves as a second copy of the site.
vercel env add NEXT_PUBLIC_SITE_URL production # https://towncenter.vercel.appDetail URLs carry a slug after the game's numeric id (/units/38-knight). The bare id is a permanent redirect to it, generated from the export in next.config.ts; src/lib/hrefs.ts is the only place that builds these paths. Social cards are drawn on demand by /api/og and cached at the edge.
Umami is optional and off by default — set NEXT_PUBLIC_UMAMI_WEBSITE_ID and the tracker loads, leave it unset and no third-party script is ever fetched. See .env.example for the full list.
cp .env.example .env.local # then fill in the website id
vercel env add NEXT_PUBLIC_UMAMI_WEBSITE_ID productionThese are inlined at build time, so a change needs a redeploy. Set NEXT_PUBLIC_UMAMI_DOMAINS to your production hostname to keep preview deployments out of the numbers. Custom events go through track() from src/lib/analytics.ts, which is a no-op whenever the tracker is absent.
MIT. Not affiliated with or endorsed by Microsoft — Age of Empires II: Definitive Edition is a Microsoft trademark.