MatchZy Auto Tournament - Architecture¶
Bracket Generation System¶
Overview¶
The bracket generation system uses a unified interface pattern that makes it easy to add new tournament types. All generators implement the IBracketGenerator interface.
Structure¶
src/services/
├── bracketGenerators/
│ ├── index.ts # Central registry mapping tournament types to generators
│ └── types.ts # IBracketGenerator interface definition
├── swissBracketGenerator.ts # Custom Swiss system implementation
├── standardBracketGenerator.ts # Single/Double elimination & Round Robin (via brackets-manager library)
└── matchConfigBuilder.ts # Builds individual match configs (used by all generators)
How It Works¶
Technical Implementation Details
1. Interface (bracketGenerators/types.ts)
interface IBracketGenerator {
generate(tournament, getMatchesCallback): Promise<BracketMatch[] | BracketGeneratorResult>;
reset?(): void; // Optional state reset
}
2. Registry (bracketGenerators/index.ts)
Maps tournament types to their generators:
const bracketGenerators: Record<TournamentType, IBracketGenerator> = {
single_elimination: standardBracketGenerator,
double_elimination: standardBracketGenerator,
round_robin: standardBracketGenerator,
swiss: swissBracketGenerator,
};
3. Usage (tournamentService.ts)
Adding New Tournament Types¶
To add a new tournament type (e.g., group_stage):
- Create the generator (implements
IBracketGenerator):
// src/services/groupStageBracketGenerator.ts
export class GroupStageBracketGenerator implements IBracketGenerator {
async generate(tournament, getMatchesCallback) {
// Your custom logic here
}
}
export const groupStageBracketGenerator = new GroupStageBracketGenerator();
- Add to type definition:
// src/types/tournament.types.ts
export type TournamentType =
| 'single_elimination'
| 'double_elimination'
| 'round_robin'
| 'swiss'
| 'group_stage'; // Add here
- Register in the registry:
// src/services/bracketGenerators/index.ts
import { groupStageBracketGenerator } from '../groupStageBracketGenerator';
export const bracketGenerators: Record<TournamentType, IBracketGenerator> = {
// ... existing
group_stage: groupStageBracketGenerator,
};
That's it! The system will automatically use your generator when a tournament of that type is created.
Current Generators¶
Standard Bracket Generator¶
- File:
standardBracketGenerator.ts - Handles:
single_elimination,double_elimination,round_robin - Implementation: Wraps the
brackets-managerlibrary - Stateful: Yes (uses in-memory storage, needs reset)
Swiss Bracket Generator¶
- File:
swissBracketGenerator.ts - Handles:
swiss - Implementation: Custom pairing algorithm
- Stateful: No (direct DB operations)
Match Config Builder¶
File: matchConfigBuilder.ts
This is not a bracket generator - it builds individual match configurations:
- Team data
- Veto state
- Map selection
- Player counts
- Side selections
All bracket generators use this to create match configs.
Benefits of This Architecture¶
- Extensible: Add new tournament types without modifying existing code
- Type-safe: TypeScript ensures all generators implement the interface correctly
- Maintainable: Each generator is isolated in its own file
- Clear separation: Generator logic vs match config logic
- Easy to test: Mock the interface for unit tests
File Naming Convention¶
*BracketGenerator.ts- Generates bracket structurematchConfigBuilder.ts- Builds individual match configsbracketGenerators/- Interface and registry
Frontend Bracket Viewer¶
The React client ships with a vendored copy of brackets-viewer.js located at client/src/brackets-viewer/. We maintain a fork because the stock package does not yet expose the behaviours we rely on:
- Extended metadata ingestion (seed positions,
nextMatchIdwiring, parent match hints) - Reactive theming hooks to map into the Material UI design system
- Smooth
zoomToElementfocus when opening our match modal - Small UX tweaks (separated BoX labels, hover states, popover styling)
When updating to a newer upstream release:
- Pull the new source from the upstream repository.
- Re-run the build script from the upstream project to regenerate assets (SCSS → CSS, TypeScript → JS).
- Copy the distribution files back into
client/src/brackets-viewer/. - Re-apply local adjustments (search for
// MatchZycomments) and runyarn lint. - Validate the bracket view for single elimination, double elimination, losers bracket transfers, and Swiss layouts.
Keeping the fork checked in ensures deterministic builds and avoids shipping multiple runtime bundles.
API Logging Controls¶
The API uses a structured Pino logger with a small set of feature flags to keep local and production logs focused on actions instead of low‑level noise (e.g. every DB query or server ping).
Environment Variables¶
LOG_LEVEL(default:infoin production,debugin local Docker)- Standard Pino log level (
debug,info,warn,error). LOG_HTTP_REQUESTS(default:true)- When
false, disables per‑request[HTTP]logs for 2xx/3xx traffic. - 4xx/5xx responses are still logged via
warn/error. LOG_DB_VERBOSE(default:false)- When
true, enables verbose[DATABASE] [DB] ...logs for every SQL query and schema statement. - When
false, only high‑level DB success logs and all errors/warnings are written. LOG_RCON_VERBOSE(default:false)- When
true, logs every successful[RCON] SUCCESS: ...command. - When
false, only failed RCON operations (and higher‑level allocation logs) are written; errors are never suppressed.
Typical Setups¶
- Local debugging (see
docker/docker-compose.local.yml): - Start verbose and trim as needed:
LOG_LEVEL=debugLOG_HTTP_REQUESTS=trueLOG_DB_VERBOSE=false(enable temporarily when debugging DB)LOG_RCON_VERBOSE=false(enable temporarily when debugging server connectivity)
- Production (
docker/docker-compose.yml): - Recommended defaults:
LOG_LEVEL=infoLOG_HTTP_REQUESTS=trueorfalsedepending on how much request audit you needLOG_DB_VERBOSE=falseLOG_RCON_VERBOSE=false
All warnings and errors are always logged regardless of these flags; the switches only affect high‑volume
info/debugcategories.