Skip to main content

Overview

The database uses PostgreSQL with Entity Framework Core as the ORM. All models are defined in Database/Models.cs:85 and inherit from a central Models class container. The schema is designed around two primary match types:
  • Elimination Matches: Head-to-head battles with pick/ban phases
  • Qualifier Lobbies: Multi-player linear mappool progression

Entity Relationship Diagram

The system implements the following database structure:
┌─────────────────┐         ┌──────────────┐
│   MatchRoom     │────────▶│    Round     │
│  (Elimination)  │         │   (Rules)    │
└─────────────────┘         └──────────────┘
        │                            ▲
        │ Red Team                   │
        ├───────────────┐            │
        │ Blue Team     │            │
        │               ▼            │
        │          ┌─────────┐       │
        │          │  User   │       │
        │          └─────────┘       │
        │               │            │
        │               │ 1:1        │
        │               ▼            │
        │          ┌──────────┐      │
        │          │ OsuUser  │      │
        │          └──────────┘      │
        │                            │
        │ Managed By                 │
        ▼                            │
   ┌─────────────┐                  │
   │ RefereeInfo │                  │
   └─────────────┘                  │
        ▲                            │
        │                            │
        │ Managed By                 │
        │                            │
┌──────────────────┐                │
│ QualifierRoom    │────────────────┘
│  (Qualifiers)    │
└──────────────────┘

        │ Requested By

   ┌─────────┐
   │  User   │
   └─────────┘

        │ Is A

   ┌─────────┐
   │ Player  │
   └─────────┘

┌──────────────┐
│ ScoreResults │
└──────────────┘
        │ Player

   ┌─────────┐
   │  User   │
   └─────────┘
The original source code includes a detailed GraphViz DOT diagram at Database/Models.cs:11-83 with color-coded relationships.

Core Entities

MatchRoom

Table: match_rooms
Purpose: Represents a scheduled elimination match between two teams
Id
string
required
Unique match identifier (e.g., “A1”, “C2”)
RoundId
int
required
Foreign key to the Round that defines rules and mappool
TeamRedId
int
required
Foreign key to User representing Team Red
TeamBlueId
int
required
Foreign key to User representing Team Blue
RefereeId
int?
Optional foreign key to assigned RefereeInfo
StartTime
DateTime?
Scheduled match start time (UTC)
EndTime
DateTime?
Actual match completion time (UTC)
BannedMaps
List<RoundChoice>
JSON-stored list of maps banned during pick/ban phase
PickedMaps
List<RoundChoice>
JSON-stored list of maps picked during the match
Bancho match history ID (e.g., https://osu.ppy.sh/community/matches/{MpLinkId})

Relationships

[ForeignKey("RoundId")]
public virtual Round Round { get; set; }

[ForeignKey("TeamRedId")]
public virtual User TeamRed { get; set; }

[ForeignKey("TeamBlueId")]
public virtual User TeamBlue { get; set; }

[ForeignKey("RefereeId")]
public virtual RefereeInfo Referee { get; set; }
{
  "Id": "A1",
  "RoundId": 3,
  "TeamRedId": 42,
  "TeamBlueId": 87,
  "RefereeId": 5,
  "StartTime": "2026-03-05T18:00:00Z",
  "BannedMaps": [
    {"Slot": "HD2", "TeamColor": "TeamRed"},
    {"Slot": "DT1", "TeamColor": "TeamBlue"}
  ],
  "PickedMaps": [
    {"Slot": "NM1", "TeamColor": "TeamRed", "Winner": "TeamRed"},
    {"Slot": "HD1", "TeamColor": "TeamBlue", "Winner": "TeamBlue"}
  ],
  "MpLinkId": 116234567
}

QualifierRoom

Table: qualifier_rooms
Purpose: Represents a qualifier lobby where multiple players compete through a fixed mappool
Id
string
required
Unique lobby identifier
RoundId
int
required
Foreign key to Round defining the qualifier mappool
StartTime
DateTime
required
Scheduled lobby start time (UTC)
RefereeId
int?
Optional assigned referee
RequestedBy
int?
User who requested this specific time slot
Approved
bool?
Whether the lobby request has been approved by staff
Bancho match history ID

Relationships

[ForeignKey("RefereeId")]
public virtual RefereeInfo Referee { get; set; }

[ForeignKey("RoundId")]
public virtual Round Round { get; set; }

[ForeignKey("RequestedBy")]
public virtual User RequestUser { get; set; }

Round

Table: rounds
Purpose: Defines tournament stage rules, mappool, and match format
Id
int
required
Unique round identifier
DisplayName
string
required
Human-readable name (e.g., “Round of 16”, “Qualifiers”)
BanRounds
int
required
Number of bans allowed per team (0 for qualifiers)
Mode
BansType
required
Ban strategy: SpanishShowdown or Other
BestOf
int
required
Maximum number of maps (e.g., 7 for Bo7, 13 for Bo13)
MapPool
List<RoundBeatmap>
required
JSON-stored list of beatmaps with slots (e.g., NM1, HD2, DT3)

Mappool Structure

public class RoundBeatmap
{
    public int BeatmapID { get; set; }  // osu! beatmap ID
    public string Slot { get; set; }     // "NM1", "HD2", "DT1", "TB1"
}
{
  "Id": 3,
  "DisplayName": "Round of 16",
  "BanRounds": 2,
  "Mode": "SpanishShowdown",
  "BestOf": 11,
  "MapPool": [
    {"BeatmapID": 3948643, "Slot": "NM1"},
    {"BeatmapID": 4127321, "Slot": "NM2"},
    {"BeatmapID": 3892456, "Slot": "HD1"},
    {"BeatmapID": 4098234, "Slot": "DT1"},
    {"BeatmapID": 4156789, "Slot": "TB1"}
  ]
}

User

Table: users
Purpose: Links osu! identity to Discord account and serves as base for players/teams
Id
int
required
Auto-incrementing primary key
OsuID
int
required
osu! user ID (unique)
DiscordID
string
Discord user snowflake ID

Virtual Properties

[ForeignKey("OsuID")]
public virtual OsuUser OsuData { get; set; }

[NotMapped]
public string DisplayName => OsuData.Username ?? "Desconocido";
The DisplayName property is computed from the related OsuUser and not stored in the database.

OsuUser

Table: osu_user
Purpose: Stores cached osu! profile data
Id
int
required
osu! user ID (matches User.OsuID)
Username
string
required
Current osu! username
GlobalRank
int
required
Global performance ranking
CountryRank
int
required
Country-specific ranking
This table should be periodically updated from the osu! API to reflect username changes and rank updates.

Player

Table: players
Purpose: Extends User with tournament registration data
Id
int
required
Auto-incrementing primary key
UserId
int
required
Foreign key to User
RegisteredAt
DateTime
required
Registration timestamp
Availability
string
required
Serialized availability schedule (format varies)
QualifierRoomId
string?
Optional foreign key to assigned QualifierRoom

Relationships

[ForeignKey("UserId")]
public virtual User User { get; set; }

[ForeignKey("QualifierRoomId")]
public virtual QualifierRoom? QualifierRoom { get; set; }

RefereeInfo

Table: referees
Purpose: Stores referee authentication credentials and identity
Id
int
required
Auto-incrementing primary key
DisplayName
string
required
osu! username used for IRC authentication
DiscordID
ulong
required
Discord user snowflake ID
OsuID
int
required
osu! user ID
IRC
string
required
IRC password for Bancho authentication
Security Critical: The IRC field contains sensitive authentication tokens. Ensure database encryption and restricted access.

ScoreResults

Table: score_results (inferred, no explicit [Table] attribute)
Purpose: Individual score entries from completed maps
Id
int
required
Auto-incrementing primary key
RoundId
int
required
Foreign key to Round
UserId
int
required
Foreign key to User (player/team)
Slot
string
required
Map slot identifier (e.g., “NM1”, “HD2”)
Score
int
required
Final score value
Accuracy
float
required
Accuracy percentage (0-100)
MaxCombo
int
required
Highest combo achieved
Grade
string
required
Performance grade (SS, S, A, B, C, D)

Relationships

[ForeignKey("UserId")]
public User Team { get; set; }

[ForeignKey("RoundId")]
public Round Round { get; set; }

Enumerations

TeamColor

Location: Database/Models.cs:92
Purpose: Indicates team/action ownership in elimination matches
public enum TeamColor
{
    TeamBlue,
    TeamRed,
    None  // Use only for initialization or neutral states
}

BansType

Location: Database/Models.cs:103
Purpose: Defines ban phase strategy
public enum BansType
{
    SpanishShowdown = 0,  // Standard snake draft or fixed order
    Other = 1
}

MatchType

Location: Database/Models.cs:113
Purpose: Differentiates between tournament modes
public enum MatchType
{
    EliminationStage = 0,
    QualifiersStage = 1
}

Supporting Data Structures

RoundChoice

Purpose: Records pick/ban decisions with ownership and outcomes
public class RoundChoice
{
    public string Slot { get; set; }         // "NM1", "HD2", etc.
    public TeamColor TeamColor { get; set; }  // Who picked/banned
    public TeamColor? Winner { get; set; }    // Who won the map (null if banned)
}
Used In:
  • MatchRoom.BannedMaps
  • MatchRoom.PickedMaps

JSON Column Storage

Entity Framework Core automatically serializes these properties to JSON:
[
  {"Slot": "HD2", "TeamColor": "TeamRed", "Winner": null},
  {"Slot": "DT1", "TeamColor": "TeamBlue", "Winner": null}
]
JSON columns provide flexibility for variable-length data like mappools without requiring separate junction tables.

Database Context

The application uses ModelsContext (Database/ModelsContext.cs) to interact with the database. Example usage from the codebase:
await using (var db = new ModelsContext())
{
    currentMatch = await db.MatchRooms.FirstAsync(m => m.Id == matchId);
    currentMatch.Referee = await db.Referees.FirstAsync(r => r.DisplayName == refDisplayName);
    currentMatch.TeamRed = await db.Users.FirstAsync(u => u.Id == currentMatch.TeamRedId);
    currentMatch.TeamBlue = await db.Users.FirstAsync(u => u.Id == currentMatch.TeamBlueId);
    currentMatch.Round = await db.Rounds.FirstAsync(r => r.Id == currentMatch.RoundId);
}
The await using pattern ensures proper disposal and connection pooling for PostgreSQL connections.

Architecture

See how these entities fit into the overall system design

Automation Overview

Learn how AutoRef interacts with match data

Build docs developers (and LLMs) love