Skip to main content
The multiplayer system enables players to compete against each other in real-time matches. It supports various match types, team modes, and advanced features like countdowns and player invitations.

Match Types

Multiplayer supports several different match formats to suit different playstyles:

Available Match Types

Players compete individually for the highest score. Each player’s performance is tracked separately.
MatchType.HeadToHead
Players are divided into teams (typically Red vs Blue). Team scores are combined to determine the winner.
MatchType.TeamVersus
Automated matchmaking system that pairs players of similar skill levels.
MatchType.Matchmaking
Competitive ranked matchmaking with skill-based ratings.
MatchType.RankedPlay

Room Settings

Multiplayer rooms are configured with various settings that control gameplay and user permissions:
public class MultiplayerRoomSettings
{
    public string Name { get; set; } = "Unnamed room";
    public string Password { get; set; } = string.Empty;
    public MatchType MatchType { get; set; } = MatchType.HeadToHead;
    public QueueMode QueueMode { get; set; } = QueueMode.HostOnly;
    public TimeSpan AutoStartDuration { get; set; }
    public bool AutoSkip { get; set; }
}

Queue Modes

Queue modes determine who can add beatmaps to the playlist:
1

Host Only

Only the room host can select and queue beatmaps.
QueueMode.HostOnly
2

All Players

Any player in the room can add beatmaps to the queue.
QueueMode.AllPlayers
3

All Players (Round Robin)

Players take turns selecting beatmaps in a round-robin fashion.
QueueMode.AllPlayersRoundRobin

Room Server Interface

The IMultiplayerRoomServer interface defines all operations available to players in a multiplayer room:

Basic Room Operations

Room Management

// Leave the current room
Task LeaveRoom();

// Transfer host to another player (host only)
Task TransferHost(int userId);

// Kick a user from the room (host only)
Task KickUser(int userId);

// Change room settings (host only)
Task ChangeSettings(MultiplayerRoomSettings settings);
Many operations are restricted to the host:
  • Transferring host
  • Kicking users
  • Changing room settings
  • Starting matches
Attempting these operations as a non-host will throw a NotHostException.

Player State Management

Players can change their state within the room:
// Change your ready/not ready state
Task ChangeState(MultiplayerUserState newState);

// Update beatmap download status
Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability);

// Change user preferences for beatmap/ruleset
Task ChangeUserStyle(int? beatmapId, int? rulesetId);

// Update selected mods
Task ChangeUserMods(IEnumerable<APIMod> newMods);
The system tracks whether each player has the required beatmap downloaded. Players without the beatmap cannot participate until the download completes.

Playlist Management

Depending on the queue mode, players can manage the playlist:
Add a new beatmap to the room’s playlist:
Task AddPlaylistItem(MultiplayerPlaylistItem item);
The item includes the beatmap, required mods, and allowed mods.
Modify an existing playlist item:
Task EditPlaylistItem(MultiplayerPlaylistItem item);
The item must have a valid ID to be edited.
Remove a beatmap from the playlist:
Task RemovePlaylistItem(long playlistItemId);

Match Control

The host controls when matches start and can abort ongoing matches:

Match State Control

// Start the match (host only)
Task StartMatch();

// Abort an ongoing match (host only)
Task AbortMatch();

// Abort your own gameplay loading
Task AbortGameplay();
1

Pre-Match

Players join the room, select mods, and mark themselves as ready.
2

Starting

Host initiates the match. All ready players begin loading the beatmap.
3

Playing

Players compete on the beatmap. Results are tracked in real-time.
4

Results

Match ends and scores are displayed. Room returns to pre-match state.

Countdown System

Rooms support countdown timers for automatic match starts:

Match Start Countdown

public class MatchStartCountdown
{
    public TimeSpan TimeRemaining { get; set; }
}
The host can configure auto-start duration in room settings:
public TimeSpan AutoStartDuration { get; set; }
public bool AutoStartEnabled => AutoStartDuration != TimeSpan.Zero;

Force Gameplay Start

A special ForceGameplayStartCountdown can override player ready states and force the match to begin after the countdown expires.

Player Invitations

Players can invite others to join their room:
Task InvitePlayer(int userId);
Invitations can fail with exceptions:
  • UserBlockedException: You or the invited user has blocked the other
  • UserBlocksPMsException: The invited user doesn’t accept private messages

Skip Voting

Players can vote to skip the beatmap intro:
Task VoteToSkipIntro();
Alternatively, the host can enable auto-skip in room settings:
public bool AutoSkip { get; set; }

Auto-Skip Behavior

When enabled, beatmap intros are automatically skipped for all players without requiring votes.

Match-Specific Requests

Different match types support custom request types:
Task SendMatchRequest(MatchUserRequest request);

Team Versus

In Team Versus mode, players can change teams:
public class ChangeTeamRequest : MatchUserRequest
{
    public MultiplayerTeam Team { get; set; }
}

Ranked Play

Ranked play includes additional features:

Ranked Play Features

  • Damage System: Card-based damage dealing
  • Stages: Progressive difficulty stages
  • User Rankings: Skill-based ratings
public class RankedPlayRoomState
{
    public RankedPlayStage Stage { get; set; }
    public List<RankedPlayUserInfo> UserInfos { get; set; }
}

Matchmaking

Quick play matchmaking tracks:
public class MatchmakingRoomState
{
    public MatchmakingStage Stage { get; set; }
    public MatchmakingUserList Users { get; set; }
    public MatchmakingRoundList Rounds { get; set; }
}

Error Handling

The multiplayer system uses specific exceptions to communicate errors:
Thrown when a non-host player attempts a host-only operation.
Thrown when attempting room operations while not in a room.
Thrown when an operation is invalid given the current game/room state.
Thrown when a requested state change is not permitted.
Thrown when attempting to join a password-protected room with incorrect password.

Best Practices

Implementation Tips

  1. Always check host status before attempting host-only operations
  2. Handle exceptions gracefully to provide good user feedback
  3. Track player states to ensure smooth match flow
  4. Validate beatmap availability before starting matches
  5. Use appropriate queue modes for your use case

Build docs developers (and LLMs) love