Skip to content
UCIK Docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

Find a match

Queue players or a party and connect them to the assigned match.

Choose a Matchmaking provider in Betide → Crossplay and finish its setup: GameLift, Edgegap, or local lobby matchmaking. Players must already be signed in.

Find a match

Queue a party
Queue a party
Preparing your graph…

Loading the interactive viewer.

Find a match and travel
Find a match and travel
Preparing your graph…

Loading the interactive viewer.

Game module dependencies: MatchmakingIntegrationKit, BetideCore, UCIKCore.

#include "Core/MIK_Subsystem.h"
#include "Engine/GameInstance.h"
#include "UCIKCore.h"

struct FMatchmakingBindings
{
    FDelegateHandle Found;
    FDelegateHandle State;
    FDelegateHandle Failed;
    FDelegateHandle Cancelled;
    FDelegateHandle TimedOut;
    FDelegateHandle Acceptance;
};

void StopWatchingMatchmaking(UGameInstance &GameInstance, FMatchmakingBindings &Bindings)
{
    if (auto *Service = GameInstance.GetSubsystem<UMIK_Subsystem>())
    {
        Service->OnMatchFoundNative.Remove(Bindings.Found);
        Service->OnMatchmakingStateChangedNative.Remove(Bindings.State);
        Service->OnMatchmakingFailedNative.Remove(Bindings.Failed);
        Service->OnMatchmakingCancelledNative.Remove(Bindings.Cancelled);
        Service->OnMatchmakingTimedOutNative.Remove(Bindings.TimedOut);
        Service->OnMatchAcceptancePendingNative.Remove(Bindings.Acceptance);
    }
    Bindings = {};
}

FGuid QueueForMatch(UGameInstance &GameInstance, const TArray<FMIK_MatchPlayer> &Players,
                    FMatchmakingBindings &Bindings)
{
    auto *Service = GameInstance.GetSubsystem<UMIK_Subsystem>();
    if (!Service)
        return {};
    StopWatchingMatchmaking(GameInstance, Bindings);
    Bindings.Found = Service->OnMatchFoundNative.AddWeakLambda(
        &GameInstance, [](const FMIK_MatchResult &Match, const FBetideError &Error) {
            if (Error.IsError())
            {
                UE_LOG(LogUCIK, Warning, TEXT("%s"), *Error.GetLogString());
                return;
            }
            UE_LOG(LogUCIK, Log, TEXT("Queue %s found match %s"), *Match.QueueHandle.ToString(),
                   *Match.MatchId);
        });
    Bindings.State = Service->OnMatchmakingStateChangedNative.AddWeakLambda(
        &GameInstance,
        [](FGuid Queue, const FMIK_MatchmakingStatus &Status, const FBetideError &Error) {
            UE_LOG(LogUCIK, Log, TEXT("%s: %s"), *Queue.ToString(), *Status.StatusMessage);
            if (Error.IsError())
                UE_LOG(LogUCIK, Warning, TEXT("%s"), *Error.GetLogString());
        });
    Bindings.Failed = Service->OnMatchmakingFailedNative.AddWeakLambda(
        &GameInstance, [](FGuid Queue, EMIK_MatchmakingState, const FBetideError &Error) {
            UE_LOG(LogUCIK, Warning, TEXT("Queue %s failed: %s"), *Queue.ToString(),
                   *Error.GetLogString());
        });
    Bindings.Cancelled = Service->OnMatchmakingCancelledNative.AddWeakLambda(
        &GameInstance, [](FGuid Queue, const FBetideError &Error) {
            UE_LOG(LogUCIK, Log, TEXT("Queue %s cancelled: %s"), *Queue.ToString(),
                   *Error.GetLogString());
        });
    Bindings.TimedOut = Service->OnMatchmakingTimedOutNative.AddWeakLambda(
        &GameInstance, [](FGuid Queue, const FBetideError &Error) {
            UE_LOG(LogUCIK, Warning, TEXT("Queue %s timed out: %s"), *Queue.ToString(),
                   *Error.GetLogString());
        });
    Bindings.Acceptance = Service->OnMatchAcceptancePendingNative.AddWeakLambda(
        &GameInstance, [](FGuid Queue, const FMIK_MatchmakingStatus &) {
            UE_LOG(LogUCIK, Log, TEXT("Queue %s is waiting for the player's answer"),
                   *Queue.ToString());
        });
    FMIK_QueueConfig Config;
    Config.QueueName = TEXT("QuickPlay");
    Config.MinPlayers = 2;
    Config.MaxPlayers = 4;
    Config.NumTeams = 0;
    Config.Players = Players;
    const FGuid Queue = Service->StartMatchmaking(Config);
    if (!Queue.IsValid())
        UE_LOG(LogUCIK, Warning, TEXT("Could not start matchmaking"));
    return Queue;
}

For the same lobby-to-player conversion as Blueprint, add UCIKNodes to your dependencies:

#include "Matchmaking/UCIK_MatchmakingLibrary.h"

FGuid QueueForParty(UGameInstance &GameInstance, const FUCIK_Lobby &PartyLobby,
                    FMatchmakingBindings &Bindings)
{
    return QueueForMatch(GameInstance,
                         UUCIK_MatchmakingLibrary::MakeMatchPlayersFromLobby(PartyLobby), Bindings);
}

Store the queue handle and C++ bindings on the owning GameInstance. Watch queue events before starting and stop watching when leaving the matchmaking screen. Events include the queue handle so your UI can route updates.

Use Start Matchmaking when your game handles the result. Find Match And Travel also handles travel; do not run both for the same request. For a party, only the leader queues the participating members. Pass a current lobby with its member list populated.

External server travel is currently blocked: the shared matchmaking path stores a complete host:port URL in Connect Info → Host and retains the port. Both travel paths append it again. This affects Edgegap and FlexMatch assignments; the compiled examples below do not prove that these connections work.

Current source exposes Error on Find Match And Travel → On Failure. The existing export predates that pin and needs refreshing after the matchmaking fixes. Its wrapper also filters callbacks by a queue handle assigned only after starting; a synchronous startup failure can be missed.

Accept, cancel, and connect

Watch queue status and acceptance
Watch queue status and acceptance
Preparing your graph…

Loading the interactive viewer.

Answer a proposed match
Answer a proposed match
Preparing your graph…

Loading the interactive viewer.

Cancel a queued match
Cancel a queued match
Preparing your graph…

Loading the interactive viewer.

Travel to the matched server
Travel to the matched server
Preparing your graph…

Loading the interactive viewer.

#include "Core/MIK_Subsystem.h"
#include "Engine/GameInstance.h"
#include "Travel/UCIKTravelSubsystem.h"
#include "UCIKCore.h"

void AnswerProposedMatch(UGameInstance &GameInstance, FGuid QueueHandle, bool bPlayerAccepted)
{
    if (auto *Service = GameInstance.GetSubsystem<UMIK_Subsystem>())
    {
        Service->AcceptMatch(QueueHandle, bPlayerAccepted,
                             FBetideCallbackVoid::CreateWeakLambda(
                                 &GameInstance, [](const TBetideResult<void> &Result) {
                                     if (!Result.bOk)
                                         UE_LOG(LogUCIK, Warning, TEXT("%s"),
                                                *Result.Error.GetLogString());
                                     else
                                         UE_LOG(LogUCIK, Log, TEXT("Match response submitted"));
                                 }));
    }
}

void CancelQueuedMatch(UGameInstance &GameInstance, FGuid QueueHandle)
{
    if (auto *Service = GameInstance.GetSubsystem<UMIK_Subsystem>())
        Service->CancelMatchmaking(QueueHandle);
}

void TravelToMatchedServer(UGameInstance &GameInstance, const FUCIK_ConnectInfo &ConnectInfo)
{
    if (auto *Service = GameInstance.GetSubsystem<UUCIKTravelSubsystem>())
    {
        Service->TravelToConnectInfo(ConnectInfo,
                                     FBetideCallback<bool>::CreateWeakLambda(
                                         &GameInstance, [](const TBetideResult<bool> &Result) {
                                             if (!Result.bOk)
                                                 UE_LOG(LogUCIK, Warning, TEXT("%s"),
                                                        *Result.Error.GetLogString());
                                             else
                                                 UE_LOG(LogUCIK, Log, TEXT("Travel requested"));
                                         }));
    }
}

Answer only after On Match Acceptance Pending and the player’s choice. The travel example requires valid Connect Info; do not pass the affected external matchmaking result until the shared fix lands. Reservation tokens need your game’s admission flow; travel does not transmit them.

For external queues, cancellation and timeout stop local processing without reporting remote ticket-deletion errors. Do not show them as confirmed backend cleanup.

Queue configuration and provider limits

An empty Matchmaking Provider uses project policy. Naming an unavailable provider fails the queue instead of silently choosing another. Match queue names and typed attributes to the provider’s profile or rule set. Supply measured latency in milliseconds when placement uses regions.

Acceptance, backfill, skill updates, and server allocation depend on the selected provider. Local queue tuning does not replace remote service rules. GetProviderOverview() reports configured provider availability; GetQueueStatusIncludingCompleted() keeps terminal results available to UI.

Was this page helpful?