Skip to content

Commit

Permalink
Several fixes; Cleaned up StoreSubsystem API and fixed issues in it; …
Browse files Browse the repository at this point in the history
…Content Subsystem now allows for partially downloading content at start;

- Fixed issues with Login (when already logged in, it'll correctly sign the user in the slot out before doing so)
- Almost done Matchmaking Docs
  • Loading branch information
PedroRauizBeamable committed Nov 20, 2024
1 parent 216c217 commit 4cadd52
Show file tree
Hide file tree
Showing 14 changed files with 307 additions and 333 deletions.
3 changes: 0 additions & 3 deletions Content/Beamable/Content/Manifests/Cooked/BCC_Global.uasset

This file was deleted.

76 changes: 76 additions & 0 deletions Docs/docs/features/matchmaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Matchmaking
## Overview

The Beamable SDK Matchmaking feature allows player to join a matchmaking queue (defined by a `UBeamGameTypeContent` instance), configure rules for matches to be made, receive notifications of progress and, optionally, provision a Game Server with a 3rd Party Game Server Orchestrator for the resulting match.

## `UBeamGameTypeContent` as Queues
Beamable's Matchmaking system depends on Beamable's [Content System](../features/content.md) in order for you to define various matchmaking queues.

Each Matchmaking queue is described by a `UBeamGameTypeContent`. This content type defines a few things about a queue:

- `TArray<FBeamMatchmakingTeamRules> Teams`: Defines the number of teams (one per entry in the array) and, for each of those teams, defines the number of players and a name.

!!! note "Dynamic Team Sizes"
This is for fixed-team-size queues. For teams that are built *after* the match is made, you can use the resulting [Lobby](../features/lobbies.md)'s data and [Federated Game Server](../guides/federations/federated-game-server.md) to compute and store the dynamic team split.

- `FOptionalBeamStatComparisonRule EntryRules`: Optionally defines a set of [Stat](../features/stats.md) comparison rules. Only players whose [Stats](../features/stats.md) match those comparisons will be allowed into this queue.

!!! note "Gating on Rank"
Failing to meet entry rule requirements will cause the Join Operation to fail --- so these can be used to gate queues on a player's account level or rank for example.

- `Numeric Rules` and `String Rules`: These are match grouping rules.
- **Numeric Rules** tries to group players with a particular stat within certain delta range.
- **String Rules** groups players whose values for a particular stat match a certain value.

!!! note "Grouping by WinRate"
If you compute and store an Win Percentage value in a `Stat`, for example, you can tell the queue to group players that are closer in win-rate than others using **Numeric Rules**.

- `MaxWaitDurationSecs`: Defines how long the player can stay in the queue without being matched; after this time passes, the matchmaking fails and `OnMatchTimedOut` is triggered.
- `MatchingIntervalSecs`: Defines the ticking interval for the queue. Defaults to 10 seconds, which means that new sets of matches are produced every 10 seconds.
- If the time it takes to tick a queue is longer than the value set here, the longer value becomes the new tick.
- `FederatedGameServerNamespace`: Defines a [Federation Id](../concepts/federation.md#federation-id) for a [Federated Game Server](../guides/federations/federated-game-server) federation.

## Joining/Leaving Queues
The **Matchmaking Subsystem** the SDK provides out of the box provides you a few things:

- A "Join a Queue" Operation.
- A "Leave a Queue" Operation.
- A "I'm in the queue, but wasn't matched yet" callback (`OnMatchSearching`)
- A "I was in the queue for too long without a match" callback (`OnMatchTimedOut`)
- A "I got matched and my match is ready" callback (`OnMatchReady`)
- A "I left the queue before getting matched" callback (`OnMatchCancelled`)

!!! warning "Party System"
Beamable's backend supports party matchmaking. The `OnMatchRemoteSearchStarted` and `OnMatchCancelled` are how a party member who is not the leader becomes aware that the leader has joined/left a queue for their party.

The SDK does not have nice ergonomics for using this yet, but it is possible to use our Party system writing your own operations on top of `UBeamPartyApi`.

Each player can only be in a single queue at a time. When joining a queue, you can optionally pass in a set of key/value pairs called `FBeamTag`. When a match gets made with that particular user/party, these tags end up inside the [Lobby](../features/lobbies.md)'s per-player data. If you're in a party, the party leader is the only one allowed to join a queue on behalf of the party.

!!! warning "Party and Tags"
When joining a queue as the party leader and passing in `FBeamTag`, those tags are only for the party leader. If you need to gather data for every user, we recommend using [Federated Game Server](../guides/federations/federated-game-server) and [Stats](../features/stats.md) to get that data into the [Lobby](../features/lobbies.md) instead.

Leaving a queue is very straight-forward; just call the function with the appropriate `FUserSlot` and at the end of the Operation the "matchmaking ticket" will be invalidated and you'll no longer be in the queue. When using this, keep in mind that the ticket is only invalidated *after* the operation completes; not after this function is called.

### Match Found and Tickets
When you join a queue in Beamable's matchmaking, you get back a `FBeamMatchmakingTicket`. This ticket contains information about the entry onto the queue:

- **GameType** is the queue type.
- **GamerTagsInTicket** hold the list of players that are in the ticket.
- **SlotsInTicket** hold the list of local `FUserSlot` that are in the ticket (just the Owner Player, unless your game has multiple local players and matchmaking).
- **FoundMatchLobbyId** is only filled inside the `OnMatchReady` callback and has the id for the resulting [Lobby](../features/lobbies.md) for the match. You can use this to retrieve data from the [Lobby Subsystem](../features/lobbies.md) inside the `OnMatchReady` callback to get connection information and more.

If you want to understand a bit more about these tickets, we recommend taking a look at the source code of the `UBeamMatchmakingSubsystem` (it is pretty simple and should give you a lot more confidence in understanding the system).
## Getting Started
To use `UBeamMatchmakingSubsystem` via blueprints (or C++), you'll need to:

- Use the [Content Window](../features/content.md) to create a `game_type` content with a single team with a Min/Max player count of 1.
- Publish that content to your realm.

???+ warning "Assumptions"
Make sure that user is logged in when the code below runs. See [Runtime Concepts](runtime-concepts.md)

- Assign delegates to `OnMatchReady`, `OnMatchCancelled`, `OnMatchTimedOut`, so on...
- Call `TryJoinQueueOperation` with the signed in `FUserSlot` and the created `game_type` content.
- After a short duration, you should see the `OnMatchReady` callback being triggered with a lobby containing just you as a player.
- That is it!
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
#include "Subsystems/Store/BeamStoreSubsystem.h"
#include "K2BeamNode_Operation_Store.generated.h"

#define LOCTEXT_NAMESPACE "UK2BeamNode_Operation_StorePerformPurchase"
#define LOCTEXT_NAMESPACE "UK2BeamNode_Operation_PerformPurchase"

UCLASS(meta=(BeamFlowNode))
class BEAMABLECOREBLUEPRINTNODES_API UK2BeamNode_Operation_StorePerformPurchase : public UK2BeamNode_Operation
class BEAMABLECOREBLUEPRINTNODES_API UK2BeamNode_Operation_PerformPurchase : public UK2BeamNode_Operation
{
GENERATED_BODY()

virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override { return LOCTEXT("Title", "Operation - Store - PerformPurchase"); }
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override { return LOCTEXT("Title", "Operation - Store - Perform Purchase"); }

virtual FName GetSubsystemSelfFunctionName() const override { return GET_FUNCTION_NAME_CHECKED(UBeamStoreSubsystem, GetSelf); }

virtual FName GetOperationFunctionName() const override { return GET_FUNCTION_NAME_CHECKED(UBeamStoreSubsystem, CommitPurchaseListingOperation); }
virtual FName GetOperationFunctionName() const override { return GET_FUNCTION_NAME_CHECKED(UBeamStoreSubsystem, PerformPurchaseOperation); }

virtual UClass* GetRuntimeSubsystemClass() const override { return UBeamStoreSubsystem::StaticClass(); }
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "AutoGen/Optionals/OptionalArrayOfBeamGamerTag.h"
#include "AutoGen/Optionals/OptionalArrayOfBeamTag.h"
#include "BeamBackend/ReplacementTypes/BeamClientPermission.h"
#include "Content/BeamContentTypes/BeamListingContent.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "PropertyType/BeamClientPermissionCustomization.h"
#include "PropertyType/BeamContentIdCustomization.h"
Expand Down Expand Up @@ -131,6 +132,13 @@ void FBeamableCoreEditorModule::StartupModule()
FOptionalBeamAccountId::StaticStruct()->GetFName(),
// this is where our MakeInstance() method is useful
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FBeamOptionalCustomization<FOptionalBeamAccountId>::MakeInstance));

PropertyModule.RegisterCustomPropertyTypeLayout(
// This is the name of the Struct this tells the property editor which is the struct property our customization will applied on.
FBeamOptionalSchedule::StaticStruct()->GetFName(),
// this is where our MakeInstance() method is useful
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FBeamOptionalCustomization<FBeamOptionalSchedule>::MakeInstance));


PropertyModule.NotifyCustomizationModuleChanged();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1355,11 +1355,15 @@ void UBeamRuntime::LoginExternalIdentity(FUserSlot UserSlot, FString ExternalSer
if (UserSlotSystem->GetUserDataAtSlot(UserSlot, RealmUser, this))
{
// Configure us to wait until the slot is fully unauthenticated and then sign in.
OnUserClearedCode.AddLambda([this, AuthSubsystem](FUserSlot UserSlot, FBeamOperationHandle OpHandle, UAuthenticateRequest* AuthReq)
UserSlotClearedEnqueuedHandle = OnUserClearedCode.AddLambda([this, AuthSubsystem](FUserSlot UserSlot, FBeamOperationHandle OpHandle, UAuthenticateRequest* AuthReq)
{
const auto AuthenticateHandler = FOnAuthenticateFullResponse::CreateUObject(this, &UBeamRuntime::OnAuthenticated, UserSlot, OpHandle, FDelayedOperation{});
FBeamRequestContext RequestContext;
AuthSubsystem->CPP_Authenticate(AuthReq, AuthenticateHandler, RequestContext, OpHandle);

// Clean Up handle
OnUserClearedCode.Remove(UserSlotClearedEnqueuedHandle);
UserSlotClearedEnqueuedHandle = {};
}, Op, Req);
UserSlotSystem->ClearUserAtSlot(UserSlot, USCR_Manual, true, this);
}
Expand All @@ -1386,11 +1390,15 @@ void UBeamRuntime::LoginEmailAndPassword(FUserSlot UserSlot, FString Email, FStr
if (UserSlotSystem->GetUserDataAtSlot(UserSlot, RealmUser, this))
{
// Configure us to wait until the slot is fully unauthenticated and then sign in.
OnUserClearedCode.AddLambda([this, AuthSubsystem](FUserSlot UserSlot, FBeamOperationHandle OpHandle, UAuthenticateRequest* AuthReq)
UserSlotClearedEnqueuedHandle = OnUserClearedCode.AddLambda([this, AuthSubsystem](FUserSlot UserSlot, FBeamOperationHandle OpHandle, UAuthenticateRequest* AuthReq)
{
const auto AuthenticateHandler = FOnAuthenticateFullResponse::CreateUObject(this, &UBeamRuntime::OnAuthenticated, UserSlot, OpHandle, FDelayedOperation{});
FBeamRequestContext RequestContext;
AuthSubsystem->CPP_Authenticate(AuthReq, AuthenticateHandler, RequestContext, OpHandle);

// Clean Up handle
OnUserClearedCode.Remove(UserSlotClearedEnqueuedHandle);
UserSlotClearedEnqueuedHandle = {};
}, Op, Req);
UserSlotSystem->ClearUserAtSlot(UserSlot, USCR_Manual, true, this);
}
Expand Down Expand Up @@ -1643,12 +1651,12 @@ void UBeamRuntime::Logout(FUserSlot UserSlot, EUserSlotClearedReason Reason, boo
FBeamRealmUser RealmUser;
if (UserSlotSystem->GetUserDataAtSlot(UserSlot, RealmUser, this))
{
// Configure us to wait until the slot is fully unauthenticated and then sign in.
FDelegateHandle Handle;
Handle = OnUserClearedCode.AddLambda([this, Op, &Handle](FUserSlot UserSlot)
// Configure us to wait until the slot is fully unauthenticated and then sign in.
UserSlotClearedEnqueuedHandle = OnUserClearedCode.AddLambda([this, Op](FUserSlot UserSlot)
{
// Remove this lambda from the list of callbacks
OnUserClearedCode.Remove(Handle);
OnUserClearedCode.Remove(UserSlotClearedEnqueuedHandle);
UserSlotClearedEnqueuedHandle = {};

// Trigger the operation success
RequestTrackerSystem->TriggerOperationSuccess(Op, UserSlot.Name);
Expand Down
Loading

0 comments on commit 4cadd52

Please sign in to comment.