Skip to content

AlexNB01/sowdraft

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

116 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SOWdraft

Web-based draft tool for Suomi OW Overwatch tournaments. Captains pick maps and ban heroes in real time through shared links, while admins control the match from a separate panel. A stream overlay is included for broadcasting.

Live at sowdraft.fi


Features

  • Real-time draft — map picks, hero bans, and side selections sync instantly across all participants via WebSockets
  • Match phases — ready check → map pick → side selection (Hybrid/Escort) → hero bans → result submission → repeat
  • Timed turns — 60-second timer per map/hero-ban turn, 30 seconds for side selection; auto-action fires on timeout
  • Result confirmation — both team captains must agree on the map score; auto-approves after 10 minutes if unanswered
  • Detailed scoring — point scores (e.g. 3-2) for Control, Escort, Flashpoint and Hybrid; win/loss for Push
  • Admin panel — create matches, edit live matches, view active games and full match history
  • Tournament support — group matches under named tournaments and filter stats/history by tournament
  • Statistics — hero ban rates, map pick rates, side selection percentages, team win/loss records
  • Discord integration — posts full match results (map-by-map breakdown with bans) to a Discord channel automatically
  • Stream overlay — dedicated read-only view for OBS/streaming software
  • Content management — add/remove maps, heroes (with images), and team logos through the admin panel

Tech stack

Layer Technology
Server Node.js 20, Express 4
Real-time Socket.IO 4
Database SQLite 3 (WAL mode)
Auth express-session, bcryptjs
Security Helmet, express-rate-limit, express-validator
File uploads Multer (memory storage)

Project structure

sowdraft/
├── server.js              # Entry point — wires middleware, routes, sockets
├── lib/
│   ├── auth.js            # requireAuth middleware + admin user
│   ├── constants.js       # Timer durations, port, etc.
│   ├── db.js              # SQLite connection + promise helpers
│   ├── discord.js         # Discord result posting
│   ├── gameState.js       # In-memory match/tournament/hero state + all game logic
│   ├── initDB.js          # Schema creation, data migration, server startup
│   ├── upload.js          # Multer image upload config
│   └── utils.js           # escapeHTML, validation middleware
├── routes/
│   ├── auth.js            # /login, /logout, /auth-status
│   ├── assets.js          # /maps, /heroes
│   ├── teams.js           # /api/teams
│   ├── mapPools.js        # /map-pools
│   ├── matches.js         # /create-match, /active-games, /match-history, admin endpoints
│   ├── tournaments.js     # /tournaments
│   └── stats.js           # /stats
├── sockets/
│   └── index.js           # All Socket.IO event handlers
├── public/
│   ├── admin.html/.js     # Admin panel (login-protected)
│   ├── user.html          # Match list / home page
│   ├── teams.html         # Captain draft view
│   ├── stream.html        # Read-only stream overlay
│   ├── match.js           # Shared client-side draft logic
│   ├── styles.css
│   └── images/            # Map thumbnails, hero portraits, team logos
└── data.db                # SQLite database (gitignored)

Setup

Prerequisites

  • Node.js 20+ (see .nvmrc)
  • npm

Install

git clone <repo-url>
cd sowdraft
npm install

Configure environment

Copy the example below to a .env file in the project root:

# Required
SESSION_SECRET=change-this-to-a-long-random-string
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change-this-password

# Discord (optional)
DISCORD_BOT_TOKEN=
DISCORD_RESULTS_CHANNEL_ID=

The server refuses to start if SESSION_SECRET, ADMIN_USERNAME, or ADMIN_PASSWORD are missing.

Run

npm start
# or on Windows:
launch.bat

The server starts on http://localhost:3000. On first launch it creates the SQLite database and seeds maps and heroes from any existing image files.


Environment variables

Variable Required Description
SESSION_SECRET Secret key for signing session cookies. Use a long random string in production.
ADMIN_USERNAME Username for the admin panel.
ADMIN_PASSWORD Password for the admin panel.
DISCORD_BOT_TOKEN Bot token for posting match results to Discord. Results are silently skipped if not set.
DISCORD_RESULTS_CHANNEL_ID Discord channel ID to post results to. Defaults to 1230883826159452342.
ASSET_VERSION Cache-busting string appended to static asset URLs. Defaults to the server start timestamp.
NODE_ENV Set to production to enable Secure flag on session cookies (requires HTTPS).

Pages and URLs

URL Who Description
/ Anyone Home — lists active matches
/admin Admin Admin panel (login required)
/teams?matchId=X&role=team1 Captain Team 1 draft view
/teams?matchId=X&role=team2 Captain Team 2 draft view
/stream?matchId=X&role=stream Streamer Read-only overlay (no sounds, no action buttons)

Links for all three roles are generated automatically when a match is created.


Match flow

Each match progresses through the following phases in order, repeating per map until a team reaches the required win count.

ready-check → map → [side-selection] → [hero-ban] → result → map → ...
                                                                    └─ complete

1. Ready check

Both team captains click Mark as ready. The draft starts when both are ready.

2. Map pick (map)

The team whose turn it is picks a map from the pool. Each gamemode can only be picked once per match (resets if all gamemodes have been used). The loser of the previous map always picks next; Team 1 picks first on the opening map.

Timer: 60 seconds. A random eligible map is picked automatically on timeout.

3. Side selection (side-selection) — Hybrid and Escort only

The team that did not pick the map chooses Attack or Defense. On the first map this defaults to the opposite team of whoever picked.

Timer: 30 seconds. A random side is chosen automatically on timeout.

4. Hero bans (hero-ban) — optional, toggled per match

One ban per team per map, applied simultaneously in a single round (first one team, then the other). Rules:

  • A hero cannot be banned by the same team in multiple maps.
  • The two bans in a round must be from different roles (Tank / DPS / Support).
  • The loser of the previous map bans first; Team 2 bans first on the opening map.

Timer: 60 seconds per ban. A random eligible hero is chosen automatically on timeout.

5. Result (result)

One team captain submits the map result:

  • Control / Escort / Flashpoint / Hybrid — enter the point score (e.g. 3-2); winner is determined from the score.
  • Push — select the winning team directly.

The opposing captain must confirm. If no confirmation arrives within 10 minutes, the result is auto-approved.

The admin can also override and submit results directly from the active games panel.


Admin panel

Access at /admin. Login credentials come from ADMIN_USERNAME / ADMIN_PASSWORD in .env. Checking Remember Me at login keeps the session alive for 30 days.

Tabs

Tab Description
Create Match Set teams, format (FT1/2/3), map pool, optional hero bans, and tournament. Generates three links (Team 1, Team 2, Stream).
Active Games Live table of ongoing matches. Edit match details, manually submit results, copy links, or end a match.
Tournaments Create and rename tournaments. Matches can be tagged with a tournament when created.
History All completed matches with map-by-map breakdowns and hero bans. Filterable by tournament.
Teams Add/remove teams and upload team logos.
Maps & Heroes Add/remove maps (with gamemode and optional thumbnail) and heroes (with role and optional portrait). Changes take effect immediately in new matches.
Statistics Aggregated hero ban counts, map pick counts, Attack vs. Defense preference, and team win/loss records. Filterable by tournament.

Discord integration

When a match completes, the server posts a formatted result to Discord:

Team A 2 - 1 Team B
🏆 Winner: Team A

**Map breakdown**
King's Row (Hybrid) Pick: Team A
**Bans**
- Team A: Tracer
- Team B: Ana
**Result: Team A (3-2)**

Nepal (Control) Pick: Team B
...

If the message exceeds Discord's 2000-character limit it is automatically split into multiple messages.

Setup:

  1. Create a Discord bot at https://discord.com/developers/applications
  2. Add the bot to your server with Send Messages permission in the target channel
  3. Set DISCORD_BOT_TOKEN and DISCORD_RESULTS_CHANNEL_ID in .env

Data storage

Everything is stored in data.db (SQLite, gitignored). The database is created automatically on first launch.

Table Contents
matches All matches (active and completed) as JSON blobs with indexed metadata
tournaments Tournament names and IDs
map_pools Saved map pool presets
maps Available maps with gamemode and optional image URL
heroes Available heroes with role and optional image URL
teams Team names and logo URLs

Uploaded images are stored in public/images/ under maps/, heroes/, or logos/ subdirectories. Filenames use UUIDs to prevent collisions.

Active match state is mirrored in memory for fast access and written to SQLite after every meaningful state change. On server restart, all state is reloaded from the database and active-game timers are resumed.


Security notes

  • Session cookies are HttpOnly, SameSite=Strict, and Secure in production
  • All admin endpoints require an authenticated session (requireAuth)
  • Login is rate-limited to 20 attempts per 15-minute window
  • Input is validated with express-validator on all write endpoints
  • DELETE /map-pools (clear all) requires { confirm: true } in the request body to prevent accidental data loss
  • Uploaded image filenames are UUIDs — user-supplied filenames are never used on disk
  • Content Security Policy is enforced via Helmet

About

Real-time map and hero draft tool for Overwatch tournaments, with captain views, stream overlay, and admin panel.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors