SDK / Unreal

Multiplayer

UnstableUnreal 5.8.4+

Brain matches players and runs your game's dedicated servers. Every online match plays on a server of your game that Brain starts for it: the server holds the match's state and alone reports how it ended. Players sign in, find a match, and connect to that server.

Ways to play

Way Players get Config
Ranked queue Skill matchmaking with visible ratings, seasons and a ladder [[matchmaking.queue]] with ranked = true
Casual queue Practice matchmaking by a hidden rating [[matchmaking.queue]]
Private match Play friends by invite code, unrated [matchmaking.private]
Open lobby Create a lobby on a server, or pick one from a list A server build
[[matchmaking.queue]]
id = "ranked"
teams = 2            # teams times team_size players, at most 16
team_size = 1
host = "server"      # your dedicated server
rating = "glicko2"   # none, elo or glicko2
ranked = true        # visible ratings; false keeps the rating hidden
guests = true        # players signed in only as guests may queue
stats = ["goals"]    # numbers your server reports per seat with the result

[[matchmaking.queue]]
id = "casual"        # a bare queue: a hidden-rating 1v1 that guests may join

[matchmaking.private]
seats = 4            # the creator included, 2 by default

ase net add ranked,casual,private,lobbies adds these sections to an existing game, and ase new-game --multiplayer writes them into a new one. Nothing goes live until your next release.

Your dedicated server

Your release publishes a Linux server build of your game alongside its clients, through the servers release target. Brain starts it on its machines when players need a match or a lobby. Regions and capacity are on your game's Settings page in the console, never in the repo.

Players and names

Online play needs the player signed in. The SDK signs in silently at startup with the platform's account where there is one (Steam, Game Center or Play Games), else with the credential this device kept, else as a new guest. Offer guests a way to link Apple or Google, so their account follows them.

  • Never log, print or record player names or ids. Showing a name on screen is fine.
  • When your game shows other players' names, it must let the player report a name and block a player (store rules). Brain ships no buttons for this: put them where your game shows names.
Unreal
Signed in OnAccountChanged
Link Apple or Google StartWebSignIn(TEXT("apple"), true)
Report a name ReportName(Name, Completion)
Block a player Block(Name, Completion)
List and lift blocks Blocked, Unblock

Blocked players are never matched together in your game's queues.

Names and blocks are C++ calls on GetPrivacy().

Find a match and travel

#include "BrainSubsystem.h"

void UMyGameInstance::FindMatch()
{
    UBrainSubsystem* Brain = GetSubsystem<UBrainSubsystem>();
    Brain->OnQueueChanged.AddDynamic(this, &UMyGameInstance::HandleQueue);
    Brain->OnMatchChanged.AddDynamic(this, &UMyGameInstance::HandleMatch);
    Brain->QueueRanked(TEXT("ranked"), UtcOffsetMinutes());
}

// UFUNCTION()
void UMyGameInstance::HandleQueue(EBrainNativeServiceControlState State, EBrainNativeFailure Failure, const FString& Json)
{
    if (State == EBrainNativeServiceControlState::Assigned)
    {
        UBrainSubsystem* Brain = GetSubsystem<UBrainSubsystem>();
        Brain->JoinMatch(EBrainNativeMatchScope::Match, Brain->GetAssignedMatch());
    }
}

// UFUNCTION()
void UMyGameInstance::HandleMatch(EBrainNativeMatchClientState State, EBrainNativeFailure Failure, const FString& TravelAddress)
{
    if (State == EBrainNativeMatchClientState::Ready)
        GetSubsystem<UBrainSubsystem>()->TravelToMatch();
}

Matches need Build set in FBrainOptions: Brain starts servers for that exact build. Gameplay runs on Unreal's own networking. A lost connection reconnects with a fresh grant on its own, and the server's state arrives again: apply it and never replay old input.

On the client, UBrainNativeSubsystem hands you the server's state before gameplay starts:

UBrainNativeSubsystem* Native = GetSubsystem<UBrainNativeSubsystem>();
Native->OnResyncSnapshot.AddDynamic(this, &UMyGameInstance::HandleSnapshot);

// UFUNCTION()
void UMyGameInstance::HandleSnapshot(const FString& Generation, const FString& Snapshot)
{
    ApplyState(Snapshot);
    GetSubsystem<UBrainNativeSubsystem>()->AcknowledgeResync(Generation);
}

CancelQueue() stops searching and LeaveMatch() leaves.

On the server

A dedicated server calls StartAuthority instead of Configure. It needs no account:

FBrainOptions Options;
Options.Game = TEXT("my-game");
Options.Build = BuildId;
Options.PlayOrigin = TEXT("https://play.asebrain.com");
Options.FleetOrigin = ServerFleetOrigin; // Fleet's private control origin, from your server's launch settings
Brain->OnAuthorityChanged.AddDynamic(this, &UMyServer::HandleAuthority);
Brain->StartAuthority(Options, /*bAcceptedProfile=*/ true);

// Give each admitted player the match's state.
Native->OnResyncRequired.AddDynamic(this, &UMyServer::HandleResync);

// UFUNCTION()
void UMyServer::HandleResync(const FString& ConnectionId, int64 Seat, const FString& Generation)
{
    Native->SendResync(ConnectionId, Generation, StateFor(Seat));
}

// When the match is decided:
Brain->SubmitMatchResult(TEXT("{\"places\":[0,1],\"stats\":{\"goals\":[2,1]}}"));

In your game mode, accept a player in Login only when UBrainNativeSubsystem::Find knows their connection and it is ready, and check the same in every server RPC. SubmitMatchResult retries until Brain settles the result; IsMatchSettled() and GetSettledResult() read it, and DrainAuthority() stops admitting players.

Open lobbies

CreateLobby() asks for an open, unranked lobby on your build. When OnLobbyChanged reports EBrainLobbyState::Ready, players join with JoinMatch(EBrainNativeMatchScope::Lobby, LobbyId). Lobbies need FleetOrigin in FBrainOptions (https://fleet.asebrain.com for players) and the player signed in.

Private matches

Private rooms are available from C++ through GetPrivateSessions(): CreateRoom, Join, View and Start. Blueprint has no private room nodes yet.

Test locally

Give each local player its own Profile in FBrainOptions. Unreal's own PktLag and PktLoss settings simulate a bad network.