Crucible: Server-Authoritative Web3 Arena Combat

A competitive 1v1 browser arena with 60 Hz server-authoritative combat, client-side prediction over a shared physics library, Socket.io + Redis scaling, and ERC-721 Gladiator NFTs on Polygon Mumbai — Sprints 0–6 shipped; Sprint 7 (polish & deploy) planned.

Highlights

  • 60 Hz authoritative simulation / 20 Hz broadcast; shared deterministic `packages/shared` physics in Node + browser
  • Progression & loot: XP to level 20, six cross-class skill trees (108 skills), crafting, salvage, match history
  • Sprint 6: FIFO matchmaking, friends & challenges, PvP WebSocket, Redis-backed adapter, input validation + rate limits
  • Template/instance equipment model: admin UI → validated bundles in Supabase Storage → in-memory `BundleLoader` at runtime
  • ERC-721 GladiatorNFT + game-server listener syncing mint events to Postgres

Tech Stack

Tags

Overview

Crucible is a competitive 1v1 arena combat game: players own Gladiators and gear as ERC-721 NFTs (Polygon Mumbai) and fight in skill-influenced real-time battles. The stack is a TypeScript monorepo — Next.js 14 frontend (Canvas 2D + prediction), a standalone Node/Express game server for authoritative simulation, shared physics/combat in packages/shared, Prisma + Supabase persistence, and Solidity contracts via Hardhat.

As of the last workflow-core sync, Sprints 0–6 are complete (auth, NFT mint, CPU + PvP combat, progression/loot, multiplayer infrastructure). Sprint 7 (E2E tests, production deploy, demo polish) is the next milestone.

Problem & Context

Many Web3 games optimize for tokens over gameplay. Crucible prioritizes combat feel first, then layers NFT ownership without forcing crypto on every player (social auth + optional wallet link). The engineering challenge is low-latency, cheat-resistant multiplayer in the browser while keeping on-chain scope minimal.

Constraints

  • Web-first: Canvas 2D at ~60 FPS; no heavy native client.
  • Testnet-only demo (Mumbai); mainnet documented but not deployed.
  • Server-authoritative: clients may predict; never decide outcomes.
  • Small team / sprint-bounded scope across seven planned sprints.

Approach & Design Decisions

  • Isomorphic shared librarypackages/shared runs the same deterministic physics in the game server and browser for prediction + reconciliation.
  • Separate game server — Long-lived Socket.io process (Railway-style hosting) avoids serverless cold starts and holds match state in memory.
  • 60 Hz sim / 20 Hz broadcast — Continuous ticks with compressed snapshots; client interpolates and reconciles.
  • Template/instance content — Equipment authored in DB, published to JSON bundles in storage, loaded once at startup (no DB mid-match).
  • Redis Socket.io adapter — Enables horizontal scaling groundwork for rooms and matchmaking.
  • Anti-cheat layering — Stamina/cooldown validation, sliding-window rate limits, server-only truth.

Implementation Highlights

  • apps/web — NextAuth (Google/Twitter), wagmi/viem mint UI, arena Canvas + useClientPrediction / useRealTimeMatch, progression UI, admin bundle authoring, quick match + friends flows.
  • apps/game-serverMatchInstance 60 Hz loop, CombatEngine, CPU AI (three HP-tier strategies), matchmaking, disconnect/reconnect window, BlockchainListener (ethers v6).
  • packages/shared — Vectors, movement, melee arcs, four weapons, projectiles, skills, crafting, loot pools.
  • contractsGladiatorNFT.sol (OpenZeppelin ERC-721, eight rolled stats at mint).

Results & Evaluation

  • Core demo criteria met: mint, equip, fight CPU, quick-match PvP, friends/challenges, authoritative outcomes, leveling + skill trees, rarity-tiered loot, crafting — per sprint documentation through v2.1.0 (multiplayer).
  • Sprint 7 still open: E2E coverage, Vercel + Railway production story, asset polish, demo video.
  • No fabricated latency or CCU metrics — extended production baselines still TODO.

Tradeoffs & Limitations

  • Scaling to very high concurrency still needs load balancer + sticky sessions beyond Redis adapter alone.
  • Placeholder art; frontend test coverage called out as Sprint 7 scope.
  • Testnet / programmer-art demo — not positioned as a production game SKU.
  • Friends surface: some GET routes noted as deferred in sprint summaries.

What I Learned

Shipping shared deterministic physics for both authority and prediction, plus WebSocket room hygiene and content bundles that remove DB reads from the hot path, is the difference between a playable arena and a fragile prototype — and keeping NFT scope narrow (ownership + mint) avoids derailing gameplay work.