Skip to main content

Overview

The SS Integrated Management Server is a distributed system that bridges Discord, osu! Bancho IRC, and a PostgreSQL database to provide automated tournament management. The system orchestrates match automation, scheduling, and real-time communication between multiple platforms.

Core Components

The system is built on four primary architectural pillars:

Discord Manager

The DiscordManager (Discord/DiscordManager.cs:15) serves as the central orchestrator that:
  • Manages the Discord bot lifecycle and slash command registration
  • Creates and manages per-match Discord threads for communication
  • Bridges messages between Discord and Bancho IRC lobbies
  • Spawns and tracks AutoRef instances for active matches
  • Maintains concurrent dictionaries of active channels and matches
// Active match tracking
private ConcurrentDictionary<string, ulong> activeChannels = new();
private ConcurrentDictionary<string, IAutoRef> activeMatches = new();
Each match gets its own dedicated Discord thread and AutoRef worker instance, ensuring isolation and independent state management.

AutoRef System

The AutoRef automation engine implements the IAutoRef interface and comes in two flavors:

Qualifier AutoRef

Manages linear mappool progression with automatic map loading and timer management

Elimination AutoRef

Handles complex pick/ban phases, timeouts, and match point detection
Both AutoRef types:
  • Connect to Bancho IRC using referee credentials
  • Parse BanchoBot messages to drive state machines
  • Execute commands automatically (!mp map, !mp start, etc.)
  • Relay all lobby activity to Discord threads
  • Persist match state to the database

Database Layer

Built on Entity Framework Core with PostgreSQL, the database stores:
  • Match schedules and configurations (MatchRoom, QualifierRoom)
  • Tournament rounds with mappools and rules (Round)
  • User and player registration data (User, OsuUser, Player)
  • Referee credentials for IRC authentication (RefereeInfo)
  • Match results and score history (ScoreResults)
See Database Schema for complete entity relationship details.

Bancho IRC Client

The system uses BanchoSharp to communicate with osu! servers:
  • Authenticates referees via IRC credentials
  • Creates tournament lobbies with MakeTournamentLobbyAsync
  • Sends multiplayer commands to control match flow
  • Listens for BanchoBot events to detect state changes
  • Parses score results from match completion messages

Technology Stack

  • .NET 9: Modern C# runtime with performance optimizations
  • Async/Await: Fully asynchronous I/O for concurrent match handling
  • Discord.Net: Discord API wrapper with interaction service
  • Slash Commands: Modern Discord command registration
  • Thread Management: Per-match isolated communication channels
  • BanchoSharp: IRC client library for osu! Bancho
  • Tournament Lobby API: !mp command automation
  • Score Parsing: Regex-based result extraction
  • PostgreSQL: Relational database for tournament data
  • Entity Framework Core: ORM with LINQ query support
  • JSON Columns: Flexible storage for picks, bans, and mappools

Data Flow for Matches

Here’s how a typical match flows through the system:

Key Flow Stages

  1. Initialization: Referee triggers match start via Discord slash command
  2. Environment Setup: DiscordManager loads DB config and spawns AutoRef worker
  3. IRC Connection: AutoRef authenticates and creates tournament lobby
  4. Automation Loop: State machine responds to BanchoBot events and team input
  5. Score Processing: Results are parsed from IRC and relayed to Discord
  6. Persistence: Final match state is saved to database on closure
The AutoRef runs asynchronously in the background. Discord messages are bridged through callbacks, not direct returns.

Communication Patterns

Discord → IRC Bridge

Messages sent in a match’s Discord thread are forwarded to the Bancho lobby:
// DiscordManager.cs:188
private async Task HandleMessageAsync(SocketMessage message)
{
    var msgToIRC = message.Content;
    bool isCommand = msgToIRC.StartsWith("!");
    bool interaction = msgToIRC.StartsWith(">");
    
    if (!isCommand && !interaction) 
        msgToIRC = $"[DISCORD | {message.Author.Username}] {message.Content}";
    
    var key = activeChannels.FirstOrDefault(m => m.Value == channelid).Key;
    await activeMatches.GetValueOrDefault(key)!.SendMessageFromDiscord(msgToIRC);
}

IRC → Discord Bridge

All lobby activity is streamed to Discord in real-time:
// DiscordManager.cs:222
private void HandleMatchIRCMessage(string matchId, string messageContent)
{
    if (activeChannels.TryGetValue(matchId, out ulong channelId))
    {
        if (client.GetChannel(channelId) is IMessageChannel channel)
        {
            await channel.SendMessageAsync($"{messageContent}");
        }
    }
}

Concurrency Model

The system handles multiple simultaneous matches using:
  • ConcurrentDictionary: Thread-safe storage for active matches
  • Independent AutoRef Workers: Each match runs in its own async context
  • Per-Match IRC Connections: Separate BanchoSharp clients per referee
  • Fire-and-Forget Tasks: Non-blocking message relay with Task.Run
The server can manage dozens of concurrent matches without interference, as each AutoRef maintains isolated state.

Entry Point

The application starts in Program.cs:16:
public static async Task Main(string[] args)
{
    Env.Load();
    CultureInfo.CurrentUICulture = new CultureInfo(
        Environment.GetEnvironmentVariable("LANGUAGE") ?? "en"
    );
    
    var services = new ServiceCollection();
    services.AddSingleton(provider => new DiscordManager(
        Environment.GetEnvironmentVariable("DISCORD_BOT_TOKEN") ?? string.Empty
    ));
    var serviceProvider = services.BuildServiceProvider();
    
    var manager = serviceProvider.GetRequiredService<DiscordManager>();
    await manager.StartAsync();
    
    await Task.Delay(-1); // Run indefinitely
}
The server uses dependency injection for the DiscordManager singleton, which orchestrates all other subsystems.

Next Steps

Database Schema

Explore entity relationships and data models

Automation Overview

Learn how state machines drive match automation

Build docs developers (and LLMs) love