Added CommonUser Plugins.
Implemented GameplayStackTags
This commit is contained in:
parent
313cf68f61
commit
7415b74e8f
38
Plugins/CommonUser/CommonUser.uplugin
Normal file
38
Plugins/CommonUser/CommonUser.uplugin
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "CommonUser",
|
||||
"Description": "Provides gameplay code and blueprint wrappers for online and platform operations.",
|
||||
"Category": "Gameplay",
|
||||
"CreatedBy": "Epic Games, Inc.",
|
||||
"CreatedByURL": "https://www.epicgames.com",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"CanContainContent": false,
|
||||
"IsBetaVersion": false,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "CommonUser",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "OnlineSubsystem",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "OnlineSubsystemUtils",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "OnlineServices",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
BIN
Plugins/CommonUser/Resources/Icon128.png
Normal file
BIN
Plugins/CommonUser/Resources/Icon128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
71
Plugins/CommonUser/Source/CommonUser/CommonUser.Build.cs
Normal file
71
Plugins/CommonUser/Source/CommonUser/CommonUser.Build.cs
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class CommonUser : ModuleRules
|
||||
{
|
||||
public CommonUser(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
bool bUseOnlineSubsystemV1 = true;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreOnline",
|
||||
"GameplayTags",
|
||||
"OnlineSubsystemUtils",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
if (bUseOnlineSubsystemV1)
|
||||
{
|
||||
PublicDependencyModuleNames.Add("OnlineSubsystem");
|
||||
}
|
||||
else
|
||||
{
|
||||
PublicDependencyModuleNames.Add("OnlineServicesInterface");
|
||||
}
|
||||
|
||||
PublicDefinitions.Add("COMMONUSER_OSSV1=" + (bUseOnlineSubsystemV1 ? "1" : "0"));
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreOnline",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"ApplicationCore",
|
||||
"InputCore",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "AsyncAction_CommonUserInitialize.h"
|
||||
|
||||
#include "GenericPlatform/GenericPlatformInputDeviceMapper.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncAction_CommonUserInitialize)
|
||||
|
||||
UAsyncAction_CommonUserInitialize* UAsyncAction_CommonUserInitialize::InitializeForLocalPlay(UCommonUserSubsystem* Target, int32 LocalPlayerIndex, FInputDeviceId PrimaryInputDevice, bool bCanUseGuestLogin)
|
||||
{
|
||||
if (!PrimaryInputDevice.IsValid())
|
||||
{
|
||||
// Set to default device
|
||||
PrimaryInputDevice = IPlatformInputDeviceMapper::Get().GetDefaultInputDevice();
|
||||
}
|
||||
|
||||
UAsyncAction_CommonUserInitialize* Action = NewObject<UAsyncAction_CommonUserInitialize>();
|
||||
|
||||
Action->RegisterWithGameInstance(Target);
|
||||
|
||||
if (Target && Action->IsRegistered())
|
||||
{
|
||||
Action->Subsystem = Target;
|
||||
|
||||
Action->Params.RequestedPrivilege = ECommonUserPrivilege::CanPlay;
|
||||
Action->Params.LocalPlayerIndex = LocalPlayerIndex;
|
||||
Action->Params.PrimaryInputDevice = PrimaryInputDevice;
|
||||
Action->Params.bCanUseGuestLogin = bCanUseGuestLogin;
|
||||
Action->Params.bCanCreateNewLocalPlayer = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Action->SetReadyToDestroy();
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
UAsyncAction_CommonUserInitialize* UAsyncAction_CommonUserInitialize::LoginForOnlinePlay(UCommonUserSubsystem* Target, int32 LocalPlayerIndex)
|
||||
{
|
||||
UAsyncAction_CommonUserInitialize* Action = NewObject<UAsyncAction_CommonUserInitialize>();
|
||||
|
||||
Action->RegisterWithGameInstance(Target);
|
||||
|
||||
if (Target && Action->IsRegistered())
|
||||
{
|
||||
Action->Subsystem = Target;
|
||||
|
||||
Action->Params.RequestedPrivilege = ECommonUserPrivilege::CanPlayOnline;
|
||||
Action->Params.LocalPlayerIndex = LocalPlayerIndex;
|
||||
Action->Params.bCanCreateNewLocalPlayer = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Action->SetReadyToDestroy();
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
void UAsyncAction_CommonUserInitialize::HandleFailure()
|
||||
{
|
||||
const UCommonUserInfo* UserInfo = nullptr;
|
||||
if (Subsystem.IsValid())
|
||||
{
|
||||
UserInfo = Subsystem->GetUserInfoForLocalPlayerIndex(Params.LocalPlayerIndex);
|
||||
}
|
||||
HandleInitializationComplete(UserInfo, false, NSLOCTEXT("CommonUser", "LoginFailedEarly", "Unable to start login process"), Params.RequestedPrivilege, Params.OnlineContext);
|
||||
}
|
||||
|
||||
void UAsyncAction_CommonUserInitialize::HandleInitializationComplete(const UCommonUserInfo* UserInfo, bool bSuccess, FText Error, ECommonUserPrivilege RequestedPrivilege, ECommonUserOnlineContext OnlineContext)
|
||||
{
|
||||
if (ShouldBroadcastDelegates())
|
||||
{
|
||||
OnInitializationComplete.Broadcast(UserInfo, bSuccess, Error, RequestedPrivilege, OnlineContext);
|
||||
}
|
||||
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
void UAsyncAction_CommonUserInitialize::Activate()
|
||||
{
|
||||
if (Subsystem.IsValid())
|
||||
{
|
||||
Params.OnUserInitializeComplete.BindUFunction(this, GET_FUNCTION_NAME_CHECKED(UAsyncAction_CommonUserInitialize, HandleInitializationComplete));
|
||||
bool bSuccess = Subsystem->TryToInitializeUser(Params);
|
||||
|
||||
if (!bSuccess)
|
||||
{
|
||||
// Call failure next frame
|
||||
FTimerManager* TimerManager = GetTimerManager();
|
||||
|
||||
if (TimerManager)
|
||||
{
|
||||
TimerManager->SetTimerForNextTick(FTimerDelegate::CreateUObject(this, &UAsyncAction_CommonUserInitialize::HandleFailure));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,120 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#include "CommonUserBasicPresence.h"
|
||||
#include "CommonSessionSubsystem.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "CommonUserTypes.h"
|
||||
|
||||
|
||||
#if COMMONUSER_OSSV1
|
||||
#include "OnlineSubsystemUtils.h"
|
||||
#include "Interfaces/OnlinePresenceInterface.h"
|
||||
#else
|
||||
#include "Online/OnlineServicesEngineUtils.h"
|
||||
#include "Online/Presence.h"
|
||||
#endif
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogUserBasicPresence, Log, All);
|
||||
DEFINE_LOG_CATEGORY(LogUserBasicPresence);
|
||||
|
||||
UCommonUserBasicPresence::UCommonUserBasicPresence()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UCommonUserBasicPresence::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
UCommonSessionSubsystem* CommonSession = Collection.InitializeDependency<UCommonSessionSubsystem>();
|
||||
if(ensure(CommonSession))
|
||||
{
|
||||
CommonSession->OnSessionInformationChangedEvent.AddUObject(this, &UCommonUserBasicPresence::OnNotifySessionInformationChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonUserBasicPresence::Deinitialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FString UCommonUserBasicPresence::SessionStateToBackendKey(ECommonSessionInformationState SessionStatus)
|
||||
{
|
||||
switch (SessionStatus)
|
||||
{
|
||||
case ECommonSessionInformationState::OutOfGame:
|
||||
return PresenceStatusMainMenu;
|
||||
break;
|
||||
case ECommonSessionInformationState::Matchmaking:
|
||||
return PresenceStatusMatchmaking;
|
||||
break;
|
||||
case ECommonSessionInformationState::InGame:
|
||||
return PresenceStatusInGame;
|
||||
break;
|
||||
default:
|
||||
UE_LOG(LogUserBasicPresence, Error, TEXT("UCommonUserBasicPresence::SessionStateToBackendKey: Found unknown enum value %d"), (uint8)SessionStatus);
|
||||
return TEXT("Unknown");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void UCommonUserBasicPresence::OnNotifySessionInformationChanged(ECommonSessionInformationState SessionStatus, const FString& GameMode, const FString& MapName)
|
||||
{
|
||||
if (bEnableSessionsBasedPresence && !GetGameInstance()->IsDedicatedServerInstance())
|
||||
{
|
||||
// trim the map name since its a URL
|
||||
FString MapNameTruncated = MapName;
|
||||
if (!MapNameTruncated.IsEmpty())
|
||||
{
|
||||
int LastIndexOfSlash = 0;
|
||||
MapNameTruncated.FindLastChar('/', LastIndexOfSlash);
|
||||
MapNameTruncated = MapNameTruncated.RightChop(LastIndexOfSlash + 1);
|
||||
}
|
||||
|
||||
#if COMMONUSER_OSSV1
|
||||
IOnlineSubsystem* OnlineSub = Online::GetSubsystem(GetWorld());
|
||||
if(OnlineSub)
|
||||
{
|
||||
IOnlinePresencePtr Presence = OnlineSub->GetPresenceInterface();
|
||||
if(Presence)
|
||||
{
|
||||
FOnlineUserPresenceStatus UpdatedPresence;
|
||||
UpdatedPresence.State = EOnlinePresenceState::Online; // We'll only send the presence update if the user has a valid UniqueNetId, so we can assume they are Online
|
||||
UpdatedPresence.StatusStr = *SessionStateToBackendKey(SessionStatus);
|
||||
UpdatedPresence.Properties.Emplace(PresenceKeyGameMode, GameMode);
|
||||
UpdatedPresence.Properties.Emplace(PresenceKeyMapName, MapNameTruncated);
|
||||
|
||||
for (const ULocalPlayer* LocalPlayer : GetGameInstance()->GetLocalPlayers())
|
||||
{
|
||||
if (LocalPlayer && LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId() != nullptr)
|
||||
{
|
||||
Presence->SetPresence(*LocalPlayer->GetPreferredUniqueNetId().GetUniqueNetId(), UpdatedPresence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
UE::Online::IOnlineServicesPtr OnlineServices = UE::Online::GetServices(GetWorld());
|
||||
check(OnlineServices);
|
||||
UE::Online::IPresencePtr Presence = OnlineServices->GetPresenceInterface();
|
||||
if(Presence)
|
||||
{
|
||||
for (const ULocalPlayer* LocalPlayer : GetGameInstance()->GetLocalPlayers())
|
||||
{
|
||||
if (LocalPlayer && LocalPlayer->GetPreferredUniqueNetId().IsV2())
|
||||
{
|
||||
UE::Online::FPartialUpdatePresence::Params UpdateParams;
|
||||
UpdateParams.LocalAccountId = LocalPlayer->GetPreferredUniqueNetId().GetV2();
|
||||
UpdateParams.Mutations.StatusString.Emplace(*SessionStateToBackendKey(SessionStatus));
|
||||
UpdateParams.Mutations.UpdatedProperties.Emplace(PresenceKeyGameMode, GameMode);
|
||||
UpdateParams.Mutations.UpdatedProperties.Emplace(PresenceKeyMapName, MapNameTruncated);
|
||||
|
||||
Presence->PartialUpdatePresence(MoveTemp(UpdateParams));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonUserModule.h"
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FCommonUserModule"
|
||||
|
||||
void FCommonUserModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
}
|
||||
|
||||
void FCommonUserModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FCommonUserModule, CommonUser)
|
2684
Plugins/CommonUser/Source/CommonUser/Private/CommonUserSubsystem.cpp
Normal file
2684
Plugins/CommonUser/Source/CommonUser/Private/CommonUserSubsystem.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "CommonUserTypes.h"
|
||||
#include "OnlineError.h"
|
||||
|
||||
void FOnlineResultInformation::FromOnlineError(const FOnlineErrorType& InOnlineError)
|
||||
{
|
||||
#if COMMONUSER_OSSV1
|
||||
bWasSuccessful = InOnlineError.WasSuccessful();
|
||||
ErrorId = InOnlineError.GetErrorCode();
|
||||
ErrorText = InOnlineError.GetErrorMessage();
|
||||
#else
|
||||
bWasSuccessful = InOnlineError != UE::Online::Errors::Success();
|
||||
ErrorId = InOnlineError.GetErrorId();
|
||||
ErrorText = InOnlineError.GetText();
|
||||
#endif
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CommonUserSubsystem.h"
|
||||
#include "Engine/CancellableAsyncAction.h"
|
||||
|
||||
#include "AsyncAction_CommonUserInitialize.generated.h"
|
||||
|
||||
enum class ECommonUserOnlineContext : uint8;
|
||||
enum class ECommonUserPrivilege : uint8;
|
||||
struct FInputDeviceId;
|
||||
|
||||
class FText;
|
||||
class UObject;
|
||||
struct FFrame;
|
||||
|
||||
/**
|
||||
* Async action to handle different functions for initializing users
|
||||
*/
|
||||
UCLASS()
|
||||
class COMMONUSER_API UAsyncAction_CommonUserInitialize : public UCancellableAsyncAction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initializes a local player with the common user system, which includes doing platform-specific login and privilege checks.
|
||||
* When the process has succeeded or failed, it will broadcast the OnInitializationComplete delegate.
|
||||
*
|
||||
* @param LocalPlayerIndex Desired index of ULocalPlayer in Game Instance, 0 will be primary player and 1+ for local multiplayer
|
||||
* @param PrimaryInputDevice Primary input device for the user, if invalid will use the system default
|
||||
* @param bCanUseGuestLogin If true, this player can be a guest without a real system net id
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser, meta = (BlueprintInternalUseOnly = "true"))
|
||||
static UAsyncAction_CommonUserInitialize* InitializeForLocalPlay(UCommonUserSubsystem* Target, int32 LocalPlayerIndex, FInputDeviceId PrimaryInputDevice, bool bCanUseGuestLogin);
|
||||
|
||||
/**
|
||||
* Attempts to log an existing user into the platform-specific online backend to enable full online play
|
||||
* When the process has succeeded or failed, it will broadcast the OnInitializationComplete delegate.
|
||||
*
|
||||
* @param LocalPlayerIndex Index of existing LocalPlayer in Game Instance
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser, meta = (BlueprintInternalUseOnly = "true"))
|
||||
static UAsyncAction_CommonUserInitialize* LoginForOnlinePlay(UCommonUserSubsystem* Target, int32 LocalPlayerIndex);
|
||||
|
||||
/** Call when initialization succeeds or fails */
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FCommonUserOnInitializeCompleteMulticast OnInitializationComplete;
|
||||
|
||||
/** Fail and send callbacks if needed */
|
||||
void HandleFailure();
|
||||
|
||||
/** Wrapper delegate, will pass on to OnInitializationComplete if appropriate */
|
||||
UFUNCTION()
|
||||
virtual void HandleInitializationComplete(const UCommonUserInfo* UserInfo, bool bSuccess, FText Error, ECommonUserPrivilege RequestedPrivilege, ECommonUserOnlineContext OnlineContext);
|
||||
|
||||
protected:
|
||||
/** Actually start the initialization */
|
||||
virtual void Activate() override;
|
||||
|
||||
TWeakObjectPtr<UCommonUserSubsystem> Subsystem;
|
||||
FCommonUserInitializeParams Params;
|
||||
};
|
@ -0,0 +1,471 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CommonUserTypes.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "UObject/ObjectPtr.h"
|
||||
#include "UObject/StrongObjectPtr.h"
|
||||
#include "UObject/PrimaryAssetId.h"
|
||||
#include "UObject/WeakObjectPtr.h"
|
||||
#include "PartyBeaconClient.h"
|
||||
#include "PartyBeaconHost.h"
|
||||
#include "PartyBeaconState.h"
|
||||
|
||||
class APlayerController;
|
||||
class AOnlineBeaconHost;
|
||||
class ULocalPlayer;
|
||||
namespace ETravelFailure { enum Type : int; }
|
||||
struct FOnlineResultInformation;
|
||||
|
||||
#if COMMONUSER_OSSV1
|
||||
#include "Interfaces/OnlineSessionInterface.h"
|
||||
#include "OnlineSessionSettings.h"
|
||||
#else
|
||||
#include "Online/Lobbies.h"
|
||||
#include "Online/OnlineAsyncOpHandle.h"
|
||||
#endif // COMMONUSER_OSSV1
|
||||
|
||||
#include "CommonSessionSubsystem.generated.h"
|
||||
|
||||
class UWorld;
|
||||
class FCommonSession_OnlineSessionSettings;
|
||||
|
||||
#if COMMONUSER_OSSV1
|
||||
class FCommonOnlineSearchSettingsOSSv1;
|
||||
using FCommonOnlineSearchSettings = FCommonOnlineSearchSettingsOSSv1;
|
||||
#else
|
||||
class FCommonOnlineSearchSettingsOSSv2;
|
||||
using FCommonOnlineSearchSettings = FCommonOnlineSearchSettingsOSSv2;
|
||||
#endif // COMMONUSER_OSSV1
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// UCommonSession_HostSessionRequest
|
||||
|
||||
/** Specifies the online features and connectivity that should be used for a game session */
|
||||
UENUM(BlueprintType)
|
||||
enum class ECommonSessionOnlineMode : uint8
|
||||
{
|
||||
Offline,
|
||||
LAN,
|
||||
Online
|
||||
};
|
||||
|
||||
/** A request object that stores the parameters used when hosting a gameplay session */
|
||||
UCLASS(BlueprintType)
|
||||
class COMMONUSER_API UCommonSession_HostSessionRequest : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Indicates if the session is a full online session or a different type */
|
||||
UPROPERTY(BlueprintReadWrite, Category=Session)
|
||||
ECommonSessionOnlineMode OnlineMode;
|
||||
|
||||
/** True if this request should create a player-hosted lobbies if available */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Session)
|
||||
bool bUseLobbies;
|
||||
|
||||
/** True if this request should create a lobby with enabled voice chat in available */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Session)
|
||||
bool bUseLobbiesVoiceChat;
|
||||
|
||||
/** True if this request should create a session that will appear in the user's presence information */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Session)
|
||||
bool bUsePresence;
|
||||
|
||||
/** String used during matchmaking to specify what type of game mode this is */
|
||||
UPROPERTY(BlueprintReadWrite, Category=Session)
|
||||
FString ModeNameForAdvertisement;
|
||||
|
||||
/** The map that will be loaded at the start of gameplay, this needs to be a valid Primary Asset top-level map */
|
||||
UPROPERTY(BlueprintReadWrite, Category=Session, meta=(AllowedTypes="World"))
|
||||
FPrimaryAssetId MapID;
|
||||
|
||||
/** Extra arguments passed as URL options to the game */
|
||||
UPROPERTY(BlueprintReadWrite, Category=Session)
|
||||
TMap<FString, FString> ExtraArgs;
|
||||
|
||||
/** Maximum players allowed per gameplay session */
|
||||
UPROPERTY(BlueprintReadWrite, Category=Session)
|
||||
int32 MaxPlayerCount = 16;
|
||||
|
||||
public:
|
||||
/** Returns the maximum players that should actually be used, could be overridden in child classes */
|
||||
virtual int32 GetMaxPlayers() const;
|
||||
|
||||
/** Returns the full map name that will be used during gameplay */
|
||||
virtual FString GetMapName() const;
|
||||
|
||||
/** Constructs the full URL that will be passed to ServerTravel */
|
||||
virtual FString ConstructTravelURL() const;
|
||||
|
||||
/** Returns true if this request is valid, returns false and logs errors if it is not */
|
||||
virtual bool ValidateAndLogErrors(FText& OutError) const;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// UCommonSession_SearchResult
|
||||
|
||||
/** A result object returned from the online system that describes a joinable game session */
|
||||
UCLASS(BlueprintType)
|
||||
class COMMONUSER_API UCommonSession_SearchResult : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Returns an internal description of the session, not meant to be human readable */
|
||||
UFUNCTION(BlueprintCallable, Category=Session)
|
||||
FString GetDescription() const;
|
||||
|
||||
/** Gets an arbitrary string setting, bFoundValue will be false if the setting does not exist */
|
||||
UFUNCTION(BlueprintPure, Category=Sessions)
|
||||
void GetStringSetting(FName Key, FString& Value, bool& bFoundValue) const;
|
||||
|
||||
/** Gets an arbitrary integer setting, bFoundValue will be false if the setting does not exist */
|
||||
UFUNCTION(BlueprintPure, Category = Sessions)
|
||||
void GetIntSetting(FName Key, int32& Value, bool& bFoundValue) const;
|
||||
|
||||
/** The number of private connections that are available */
|
||||
UFUNCTION(BlueprintPure, Category=Sessions)
|
||||
int32 GetNumOpenPrivateConnections() const;
|
||||
|
||||
/** The number of publicly available connections that are available */
|
||||
UFUNCTION(BlueprintPure, Category=Sessions)
|
||||
int32 GetNumOpenPublicConnections() const;
|
||||
|
||||
/** The maximum number of publicly available connections that could be available, including already filled connections */
|
||||
UFUNCTION(BlueprintPure, Category = Sessions)
|
||||
int32 GetMaxPublicConnections() const;
|
||||
|
||||
/** Ping to the search result, MAX_QUERY_PING is unreachable */
|
||||
UFUNCTION(BlueprintPure, Category=Sessions)
|
||||
int32 GetPingInMs() const;
|
||||
|
||||
public:
|
||||
/** Pointer to the platform-specific implementation */
|
||||
#if COMMONUSER_OSSV1
|
||||
FOnlineSessionSearchResult Result;
|
||||
#else
|
||||
TSharedPtr<const UE::Online::FLobby> Lobby;
|
||||
#endif // COMMONUSER_OSSV1
|
||||
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// UCommonSession_SearchSessionRequest
|
||||
|
||||
/** Delegates called when a session search completes */
|
||||
DECLARE_MULTICAST_DELEGATE_TwoParams(FCommonSession_FindSessionsFinished, bool bSucceeded, const FText& ErrorMessage);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FCommonSession_FindSessionsFinishedDynamic, bool, bSucceeded, FText, ErrorMessage);
|
||||
|
||||
/** Request object describing a session search, this object will be updated once the search has completed */
|
||||
UCLASS(BlueprintType)
|
||||
class COMMONUSER_API UCommonSession_SearchSessionRequest : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Indicates if the this is looking for full online games or a different type like LAN */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Session)
|
||||
ECommonSessionOnlineMode OnlineMode;
|
||||
|
||||
/** True if this request should look for player-hosted lobbies if they are available, false will only search for registered server sessions */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Session)
|
||||
bool bUseLobbies;
|
||||
|
||||
/** List of all found sessions, will be valid when OnSearchFinished is called */
|
||||
UPROPERTY(BlueprintReadOnly, Category=Session)
|
||||
TArray<TObjectPtr<UCommonSession_SearchResult>> Results;
|
||||
|
||||
/** Native Delegate called when a session search completes */
|
||||
FCommonSession_FindSessionsFinished OnSearchFinished;
|
||||
|
||||
/** Called by subsystem to execute finished delegates */
|
||||
void NotifySearchFinished(bool bSucceeded, const FText& ErrorMessage);
|
||||
|
||||
private:
|
||||
/** Delegate called when a session search completes */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Events", meta = (DisplayName = "On Search Finished", AllowPrivateAccess = true))
|
||||
FCommonSession_FindSessionsFinishedDynamic K2_OnSearchFinished;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// CommonSessionSubsystem Events
|
||||
|
||||
/**
|
||||
* Event triggered when the local user has requested to join a session from an external source, for example from a platform overlay.
|
||||
* Generally, the game should transition the player into the session.
|
||||
* @param LocalPlatformUserId the local user id that accepted the invitation. This is a platform user id because the user might not be signed in yet.
|
||||
* @param RequestedSession the requested session. Can be null if there was an error processing the request.
|
||||
* @param RequestedSessionResult result of the requested session processing
|
||||
*/
|
||||
DECLARE_MULTICAST_DELEGATE_ThreeParams(FCommonSessionOnUserRequestedSession, const FPlatformUserId& /*LocalPlatformUserId*/, UCommonSession_SearchResult* /*RequestedSession*/, const FOnlineResultInformation& /*RequestedSessionResult*/);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FCommonSessionOnUserRequestedSession_Dynamic, const FPlatformUserId&, LocalPlatformUserId, UCommonSession_SearchResult*, RequestedSession, const FOnlineResultInformation&, RequestedSessionResult);
|
||||
|
||||
/**
|
||||
* Event triggered when a session join has completed, after joining the underlying session and before traveling to the server if it was successful.
|
||||
* The event parameters indicate if this was successful, or if there was an error that will stop it from traveling.
|
||||
* @param Result result of the session join
|
||||
*/
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FCommonSessionOnJoinSessionComplete, const FOnlineResultInformation& /*Result*/);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCommonSessionOnJoinSessionComplete_Dynamic, const FOnlineResultInformation&, Result);
|
||||
|
||||
/**
|
||||
* Event triggered when a session creation for hosting has completed, right before it travels to the map.
|
||||
* The event parameters indicate if this was successful, or if there was an error that will stop it from traveling.
|
||||
* @param Result result of the session join
|
||||
*/
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FCommonSessionOnCreateSessionComplete, const FOnlineResultInformation& /*Result*/);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FCommonSessionOnCreateSessionComplete_Dynamic, const FOnlineResultInformation&, Result);
|
||||
|
||||
/**
|
||||
* Event triggered when the local user has requested to destroy a session from an external source, for example from a platform overlay.
|
||||
* The game should transition the player out of the session.
|
||||
* @param LocalPlatformUserId the local user id that made the destroy request. This is a platform user id because the user might not be signed in yet.
|
||||
* @param SessionName the name identifier for the session.
|
||||
*/
|
||||
DECLARE_MULTICAST_DELEGATE_TwoParams(FCommonSessionOnDestroySessionRequested, const FPlatformUserId& /*LocalPlatformUserId*/, const FName& /*SessionName*/);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FCommonSessionOnDestroySessionRequested_Dynamic, const FPlatformUserId&, LocalPlatformUserId, const FName&, SessionName);
|
||||
|
||||
/**
|
||||
* Event triggered when a session join has completed, after resolving the connect string and prior to the client traveling.
|
||||
* @param URL resolved connection string for the session with any additional arguments
|
||||
*/
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FCommonSessionOnPreClientTravel, FString& /*URL*/);
|
||||
|
||||
/**
|
||||
* Event triggered at different points in the session ecosystem that represent a user-presentable state of the session.
|
||||
* This should not be used for online functionality (use OnCreateSessionComplete or OnJoinSessionComplete for those) but for features such as rich presence
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class ECommonSessionInformationState : uint8
|
||||
{
|
||||
OutOfGame,
|
||||
Matchmaking,
|
||||
InGame
|
||||
};
|
||||
DECLARE_MULTICAST_DELEGATE_ThreeParams(FCommonSessionOnSessionInformationChanged, ECommonSessionInformationState /*SessionStatus*/, const FString& /*GameMode*/, const FString& /*MapName*/);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FCommonSessionOnSessionInformationChanged_Dynamic, ECommonSessionInformationState, SessionStatus, const FString&, GameMode, const FString&, MapName);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// UCommonSessionSubsystem
|
||||
|
||||
/**
|
||||
* Game subsystem that handles requests for hosting and joining online games.
|
||||
* One subsystem is created for each game instance and can be accessed from blueprints or C++ code.
|
||||
* If a game-specific subclass exists, this base subsystem will not be created.
|
||||
*/
|
||||
UCLASS(BlueprintType, Config=Engine)
|
||||
class COMMONUSER_API UCommonSessionSubsystem : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonSessionSubsystem() { }
|
||||
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
|
||||
|
||||
/** Creates a host session request with default options for online games, this can be modified after creation */
|
||||
UFUNCTION(BlueprintCallable, Category = Session)
|
||||
virtual UCommonSession_HostSessionRequest* CreateOnlineHostSessionRequest();
|
||||
|
||||
/** Creates a session search object with default options to look for default online games, this can be modified after creation */
|
||||
UFUNCTION(BlueprintCallable, Category = Session)
|
||||
virtual UCommonSession_SearchSessionRequest* CreateOnlineSearchSessionRequest();
|
||||
|
||||
/** Creates a new online game using the session request information, if successful this will start a hard map transfer */
|
||||
UFUNCTION(BlueprintCallable, Category=Session)
|
||||
virtual void HostSession(APlayerController* HostingPlayer, UCommonSession_HostSessionRequest* Request);
|
||||
|
||||
/** Starts a process to look for existing sessions or create a new one if no viable sessions are found */
|
||||
UFUNCTION(BlueprintCallable, Category=Session)
|
||||
virtual void QuickPlaySession(APlayerController* JoiningOrHostingPlayer, UCommonSession_HostSessionRequest* Request);
|
||||
|
||||
/** Starts process to join an existing session, if successful this will connect to the specified server */
|
||||
UFUNCTION(BlueprintCallable, Category=Session)
|
||||
virtual void JoinSession(APlayerController* JoiningPlayer, UCommonSession_SearchResult* Request);
|
||||
|
||||
/** Queries online system for the list of joinable sessions matching the search request */
|
||||
UFUNCTION(BlueprintCallable, Category=Session)
|
||||
virtual void FindSessions(APlayerController* SearchingPlayer, UCommonSession_SearchSessionRequest* Request);
|
||||
|
||||
/** Clean up any active sessions, called from cases like returning to the main menu */
|
||||
UFUNCTION(BlueprintCallable, Category=Session)
|
||||
virtual void CleanUpSessions();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Events
|
||||
|
||||
/** Native Delegate when a local user has accepted an invite */
|
||||
FCommonSessionOnUserRequestedSession OnUserRequestedSessionEvent;
|
||||
/** Event broadcast when a local user has accepted an invite */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Events", meta = (DisplayName = "On User Requested Session"))
|
||||
FCommonSessionOnUserRequestedSession_Dynamic K2_OnUserRequestedSessionEvent;
|
||||
|
||||
/** Native Delegate when a JoinSession call has completed */
|
||||
FCommonSessionOnJoinSessionComplete OnJoinSessionCompleteEvent;
|
||||
/** Event broadcast when a JoinSession call has completed */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Events", meta = (DisplayName = "On Join Session Complete"))
|
||||
FCommonSessionOnJoinSessionComplete_Dynamic K2_OnJoinSessionCompleteEvent;
|
||||
|
||||
/** Native Delegate when a CreateSession call has completed */
|
||||
FCommonSessionOnCreateSessionComplete OnCreateSessionCompleteEvent;
|
||||
/** Event broadcast when a CreateSession call has completed */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Events", meta = (DisplayName = "On Create Session Complete"))
|
||||
FCommonSessionOnCreateSessionComplete_Dynamic K2_OnCreateSessionCompleteEvent;
|
||||
|
||||
/** Native Delegate when the presentable session information has changed */
|
||||
FCommonSessionOnSessionInformationChanged OnSessionInformationChangedEvent;
|
||||
/** Event broadcast when the presentable session information has changed */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Events", meta = (DisplayName = "On Session Information Changed"))
|
||||
FCommonSessionOnSessionInformationChanged_Dynamic K2_OnSessionInformationChangedEvent;
|
||||
|
||||
/** Native Delegate when a platform session destroy has been requested */
|
||||
FCommonSessionOnDestroySessionRequested OnDestroySessionRequestedEvent;
|
||||
/** Event broadcast when a platform session destroy has been requested */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Events", meta = (DisplayName = "On Leave Session Requested"))
|
||||
FCommonSessionOnDestroySessionRequested_Dynamic K2_OnDestroySessionRequestedEvent;
|
||||
|
||||
/** Native Delegate for modifying the connect URL prior to a client travel */
|
||||
FCommonSessionOnPreClientTravel OnPreClientTravelEvent;
|
||||
|
||||
// Config settings, these can overridden in child classes or config files
|
||||
|
||||
/** Sets the default value of bUseLobbies for session search and host requests */
|
||||
UPROPERTY(Config)
|
||||
bool bUseLobbiesDefault = true;
|
||||
|
||||
/** Sets the default value of bUseLobbiesVoiceChat for session host requests */
|
||||
UPROPERTY(Config)
|
||||
bool bUseLobbiesVoiceChatDefault = false;
|
||||
|
||||
/** Enables reservation beacon flow prior to server travel when creating or joining a game session */
|
||||
UPROPERTY(Config)
|
||||
bool bUseBeacons = true;
|
||||
|
||||
protected:
|
||||
// Functions called during the process of creating or joining a session, these can be overidden for game-specific behavior
|
||||
|
||||
/** Called to fill in a session request from quick play host settings, can be overridden for game-specific behavior */
|
||||
virtual TSharedRef<FCommonOnlineSearchSettings> CreateQuickPlaySearchSettings(UCommonSession_HostSessionRequest* Request, UCommonSession_SearchSessionRequest* QuickPlayRequest);
|
||||
|
||||
/** Called when a quick play search finishes, can be overridden for game-specific behavior */
|
||||
virtual void HandleQuickPlaySearchFinished(bool bSucceeded, const FText& ErrorMessage, TWeakObjectPtr<APlayerController> JoiningOrHostingPlayer, TStrongObjectPtr<UCommonSession_HostSessionRequest> HostRequest);
|
||||
|
||||
/** Called when traveling to a session fails */
|
||||
virtual void TravelLocalSessionFailure(UWorld* World, ETravelFailure::Type FailureType, const FString& ReasonString);
|
||||
|
||||
/** Called when a new session is either created or fails to be created */
|
||||
virtual void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);
|
||||
|
||||
/** Called to finalize session creation */
|
||||
virtual void FinishSessionCreation(bool bWasSuccessful);
|
||||
|
||||
/** Called after traveling to the new hosted session map */
|
||||
virtual void HandlePostLoadMap(UWorld* World);
|
||||
|
||||
protected:
|
||||
// Internal functions for initializing and handling results from the online systems
|
||||
|
||||
void BindOnlineDelegates();
|
||||
void CreateOnlineSessionInternal(ULocalPlayer* LocalPlayer, UCommonSession_HostSessionRequest* Request);
|
||||
void FindSessionsInternal(APlayerController* SearchingPlayer, const TSharedRef<FCommonOnlineSearchSettings>& InSearchSettings);
|
||||
void JoinSessionInternal(ULocalPlayer* LocalPlayer, UCommonSession_SearchResult* Request);
|
||||
void InternalTravelToSession(const FName SessionName);
|
||||
void NotifyUserRequestedSession(const FPlatformUserId& PlatformUserId, UCommonSession_SearchResult* RequestedSession, const FOnlineResultInformation& RequestedSessionResult);
|
||||
void NotifyJoinSessionComplete(const FOnlineResultInformation& Result);
|
||||
void NotifyCreateSessionComplete(const FOnlineResultInformation& Result);
|
||||
void NotifySessionInformationUpdated(ECommonSessionInformationState SessionStatusStr, const FString& GameMode = FString(), const FString& MapName = FString());
|
||||
void NotifyDestroySessionRequested(const FPlatformUserId& PlatformUserId, const FName& SessionName);
|
||||
void SetCreateSessionError(const FText& ErrorText);
|
||||
|
||||
#if COMMONUSER_OSSV1
|
||||
void BindOnlineDelegatesOSSv1();
|
||||
void CreateOnlineSessionInternalOSSv1(ULocalPlayer* LocalPlayer, UCommonSession_HostSessionRequest* Request);
|
||||
void FindSessionsInternalOSSv1(ULocalPlayer* LocalPlayer);
|
||||
void JoinSessionInternalOSSv1(ULocalPlayer* LocalPlayer, UCommonSession_SearchResult* Request);
|
||||
TSharedRef<FCommonOnlineSearchSettings> CreateQuickPlaySearchSettingsOSSv1(UCommonSession_HostSessionRequest* Request, UCommonSession_SearchSessionRequest* QuickPlayRequest);
|
||||
void CleanUpSessionsOSSv1();
|
||||
|
||||
void HandleSessionFailure(const FUniqueNetId& NetId, ESessionFailure::Type FailureType);
|
||||
void HandleSessionUserInviteAccepted(const bool bWasSuccessful, const int32 LocalUserIndex, FUniqueNetIdPtr AcceptingUserId, const FOnlineSessionSearchResult& SearchResult);
|
||||
void OnStartSessionComplete(FName SessionName, bool bWasSuccessful);
|
||||
void OnRegisterLocalPlayerComplete_CreateSession(const FUniqueNetId& PlayerId, EOnJoinSessionCompleteResult::Type Result);
|
||||
void OnUpdateSessionComplete(FName SessionName, bool bWasSuccessful);
|
||||
void OnEndSessionComplete(FName SessionName, bool bWasSuccessful);
|
||||
void OnDestroySessionComplete(FName SessionName, bool bWasSuccessful);
|
||||
void OnDestroySessionRequested(int32 LocalUserNum, FName SessionName);
|
||||
void OnFindSessionsComplete(bool bWasSuccessful);
|
||||
void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result);
|
||||
void OnRegisterJoiningLocalPlayerComplete(const FUniqueNetId& PlayerId, EOnJoinSessionCompleteResult::Type Result);
|
||||
void FinishJoinSession(EOnJoinSessionCompleteResult::Type Result);
|
||||
|
||||
#else
|
||||
void BindOnlineDelegatesOSSv2();
|
||||
void CreateOnlineSessionInternalOSSv2(ULocalPlayer* LocalPlayer, UCommonSession_HostSessionRequest* Request);
|
||||
void FindSessionsInternalOSSv2(ULocalPlayer* LocalPlayer);
|
||||
void JoinSessionInternalOSSv2(ULocalPlayer* LocalPlayer, UCommonSession_SearchResult* Request);
|
||||
TSharedRef<FCommonOnlineSearchSettings> CreateQuickPlaySearchSettingsOSSv2(UCommonSession_HostSessionRequest* HostRequest, UCommonSession_SearchSessionRequest* SearchRequest);
|
||||
void CleanUpSessionsOSSv2();
|
||||
|
||||
/** Process a join request originating from the online service */
|
||||
void OnSessionJoinRequested(const UE::Online::FUILobbyJoinRequested& EventParams);
|
||||
|
||||
/** Get the local user id for a given controller */
|
||||
UE::Online::FAccountId GetAccountId(APlayerController* PlayerController) const;
|
||||
/** Get the lobby id for a given session name */
|
||||
UE::Online::FLobbyId GetLobbyId(const FName SessionName) const;
|
||||
/** Event handle for UI lobby join requested */
|
||||
UE::Online::FOnlineEventDelegateHandle LobbyJoinRequestedHandle;
|
||||
#endif // COMMONUSER_OSSV1
|
||||
|
||||
void CreateHostReservationBeacon();
|
||||
void ConnectToHostReservationBeacon();
|
||||
void DestroyHostReservationBeacon();
|
||||
|
||||
protected:
|
||||
/** The travel URL that will be used after session operations are complete */
|
||||
FString PendingTravelURL;
|
||||
|
||||
/** Most recent result information for a session creation attempt, stored here to allow storing error codes for later */
|
||||
FOnlineResultInformation CreateSessionResult;
|
||||
|
||||
/** True if we want to cancel the session after it is created */
|
||||
bool bWantToDestroyPendingSession = false;
|
||||
|
||||
/** True if this is a dedicated server, which doesn't require a LocalPlayer to create a session */
|
||||
bool bIsDedicatedServer = false;
|
||||
|
||||
/** Settings for the current search */
|
||||
TSharedPtr<FCommonOnlineSearchSettings> SearchSettings;
|
||||
|
||||
/** General beacon listener for registering beacons with */
|
||||
UPROPERTY(Transient)
|
||||
TWeakObjectPtr<AOnlineBeaconHost> BeaconHostListener;
|
||||
/** State of the beacon host */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UPartyBeaconState> ReservationBeaconHostState;
|
||||
/** Beacon controlling access to this game. */
|
||||
UPROPERTY(Transient)
|
||||
TWeakObjectPtr<APartyBeaconHost> ReservationBeaconHost;
|
||||
/** Common class object for beacon communication */
|
||||
UPROPERTY(Transient)
|
||||
TWeakObjectPtr<APartyBeaconClient> ReservationBeaconClient;
|
||||
|
||||
/** Number of teams for beacon reservation */
|
||||
UPROPERTY(Config)
|
||||
int32 BeaconTeamCount = 2;
|
||||
/** Size of a team for beacon reservation */
|
||||
UPROPERTY(Config)
|
||||
int32 BeaconTeamSize = 8;
|
||||
/** Max number of beacon reservations */
|
||||
UPROPERTY(Config)
|
||||
int32 BeaconMaxReservations = 16;
|
||||
};
|
@ -0,0 +1,59 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
#pragma once
|
||||
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "CommonUserBasicPresence.generated.h"
|
||||
|
||||
class UCommonSessionSubsystem;
|
||||
enum class ECommonSessionInformationState : uint8;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// UCommonUserBasicPresence
|
||||
|
||||
/**
|
||||
* This subsystem plugs into the session subsystem and pushes its information to the presence interface.
|
||||
* It is not intended to be a full featured rich presence implementation, but can be used as a proof-of-concept
|
||||
* for pushing information from the session subsystem to the presence system
|
||||
*/
|
||||
UCLASS(BlueprintType, Config = Engine)
|
||||
class COMMONUSER_API UCommonUserBasicPresence : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonUserBasicPresence();
|
||||
|
||||
|
||||
/** Implement this for initialization of instances of the system */
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
|
||||
/** Implement this for deinitialization of instances of the system */
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
/** False is a general purpose killswitch to stop this class from pushing presence*/
|
||||
UPROPERTY(Config)
|
||||
bool bEnableSessionsBasedPresence = false;
|
||||
|
||||
/** Maps the presence status "In-game" to a backend key*/
|
||||
UPROPERTY(Config)
|
||||
FString PresenceStatusInGame;
|
||||
|
||||
/** Maps the presence status "Main Menu" to a backend key*/
|
||||
UPROPERTY(Config)
|
||||
FString PresenceStatusMainMenu;
|
||||
|
||||
/** Maps the presence status "Matchmaking" to a backend key*/
|
||||
UPROPERTY(Config)
|
||||
FString PresenceStatusMatchmaking;
|
||||
|
||||
/** Maps the "Game Mode" rich presence entry to a backend key*/
|
||||
UPROPERTY(Config)
|
||||
FString PresenceKeyGameMode;
|
||||
|
||||
/** Maps the "Map Name" rich presence entry to a backend key*/
|
||||
UPROPERTY(Config)
|
||||
FString PresenceKeyMapName;
|
||||
|
||||
void OnNotifySessionInformationChanged(ECommonSessionInformationState SessionStatus, const FString& GameMode, const FString& MapName);
|
||||
FString SessionStateToBackendKey(ECommonSessionInformationState SessionStatus);
|
||||
};
|
@ -0,0 +1,14 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleInterface.h"
|
||||
|
||||
class FCommonUserModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
@ -0,0 +1,649 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CommonUserTypes.h"
|
||||
#include "Engine/GameViewportClient.h"
|
||||
#include "GameFramework/OnlineReplStructs.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "UObject/WeakObjectPtr.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "CommonUserSubsystem.generated.h"
|
||||
|
||||
#if COMMONUSER_OSSV1
|
||||
#include "Interfaces/OnlineIdentityInterface.h"
|
||||
#include "OnlineError.h"
|
||||
#else
|
||||
#include "Online/OnlineAsyncOpHandle.h"
|
||||
#endif
|
||||
|
||||
class FNativeGameplayTag;
|
||||
class IOnlineSubsystem;
|
||||
|
||||
/** List of tags used by the common user subsystem */
|
||||
struct COMMONUSER_API FCommonUserTags
|
||||
{
|
||||
// General severity levels and specific system messages
|
||||
|
||||
static FNativeGameplayTag SystemMessage_Error; // SystemMessage.Error
|
||||
static FNativeGameplayTag SystemMessage_Warning; // SystemMessage.Warning
|
||||
static FNativeGameplayTag SystemMessage_Display; // SystemMessage.Display
|
||||
|
||||
/** All attempts to initialize a player failed, user has to do something before trying again */
|
||||
static FNativeGameplayTag SystemMessage_Error_InitializeLocalPlayerFailed; // SystemMessage.Error.InitializeLocalPlayerFailed
|
||||
|
||||
|
||||
// Platform trait tags, it is expected that the game instance or other system calls SetTraitTags with these tags for the appropriate platform
|
||||
|
||||
/** This tag means it is a console platform that directly maps controller IDs to different system users. If false, the same user can have multiple controllers */
|
||||
static FNativeGameplayTag Platform_Trait_RequiresStrictControllerMapping; // Platform.Trait.RequiresStrictControllerMapping
|
||||
|
||||
/** This tag means the platform has a single online user and all players use index 0 */
|
||||
static FNativeGameplayTag Platform_Trait_SingleOnlineUser; // Platform.Trait.SingleOnlineUser
|
||||
};
|
||||
|
||||
/** Logical representation of an individual user, one of these will exist for all initialized local players */
|
||||
UCLASS(BlueprintType)
|
||||
class COMMONUSER_API UCommonUserInfo : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Primary controller input device for this user, they could also have additional secondary devices */
|
||||
UPROPERTY(BlueprintReadOnly, Category = UserInfo)
|
||||
FInputDeviceId PrimaryInputDevice;
|
||||
|
||||
/** Specifies the logical user on the local platform, guest users will point to the primary user */
|
||||
UPROPERTY(BlueprintReadOnly, Category = UserInfo)
|
||||
FPlatformUserId PlatformUser;
|
||||
|
||||
/** If this user is assigned a LocalPlayer, this will match the index in the GameInstance localplayers array once it is fully created */
|
||||
UPROPERTY(BlueprintReadOnly, Category = UserInfo)
|
||||
int32 LocalPlayerIndex = -1;
|
||||
|
||||
/** If true, this user is allowed to be a guest */
|
||||
UPROPERTY(BlueprintReadOnly, Category = UserInfo)
|
||||
bool bCanBeGuest = false;
|
||||
|
||||
/** If true, this is a guest user attached to primary user 0 */
|
||||
UPROPERTY(BlueprintReadOnly, Category = UserInfo)
|
||||
bool bIsGuest = false;
|
||||
|
||||
/** Overall state of the user's initialization process */
|
||||
UPROPERTY(BlueprintReadOnly, Category = UserInfo)
|
||||
ECommonUserInitializationState InitializationState = ECommonUserInitializationState::Invalid;
|
||||
|
||||
/** Returns true if this user has successfully logged in */
|
||||
UFUNCTION(BlueprintCallable, Category = UserInfo)
|
||||
bool IsLoggedIn() const;
|
||||
|
||||
/** Returns true if this user is in the middle of logging in */
|
||||
UFUNCTION(BlueprintCallable, Category = UserInfo)
|
||||
bool IsDoingLogin() const;
|
||||
|
||||
/** Returns the most recently queries result for a specific privilege, will return unknown if never queried */
|
||||
UFUNCTION(BlueprintCallable, Category = UserInfo)
|
||||
ECommonUserPrivilegeResult GetCachedPrivilegeResult(ECommonUserPrivilege Privilege, ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Ask about the general availability of a feature, this combines cached results with state */
|
||||
UFUNCTION(BlueprintCallable, Category = UserInfo)
|
||||
ECommonUserAvailability GetPrivilegeAvailability(ECommonUserPrivilege Privilege) const;
|
||||
|
||||
/** Returns the net id for the given context */
|
||||
UFUNCTION(BlueprintCallable, Category = UserInfo)
|
||||
FUniqueNetIdRepl GetNetId(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Returns the user's human readable nickname, this will return the value that was cached during UpdateCachedNetId or SetNickname */
|
||||
UFUNCTION(BlueprintCallable, Category = UserInfo)
|
||||
FString GetNickname(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Modify the user's human readable nickname, this can be used when setting up multiple guests but will get overwritten with the platform nickname for real users */
|
||||
UFUNCTION(BlueprintCallable, Category = UserInfo)
|
||||
void SetNickname(const FString& NewNickname, ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game);
|
||||
|
||||
/** Returns an internal debug string for this player */
|
||||
UFUNCTION(BlueprintCallable, Category = UserInfo)
|
||||
FString GetDebugString() const;
|
||||
|
||||
/** Accessor for platform user id */
|
||||
FPlatformUserId GetPlatformUserId() const;
|
||||
|
||||
/** Gets the platform user index for older functions expecting an integer */
|
||||
int32 GetPlatformUserIndex() const;
|
||||
|
||||
// Internal data, only intended to be accessed by online subsystems
|
||||
|
||||
/** Cached data for each online system */
|
||||
struct FCachedData
|
||||
{
|
||||
/** Cached net id per system */
|
||||
FUniqueNetIdRepl CachedNetId;
|
||||
|
||||
/** Cached nickanem, updated whenever net ID might change */
|
||||
FString CachedNickname;
|
||||
|
||||
/** Cached values of various user privileges */
|
||||
TMap<ECommonUserPrivilege, ECommonUserPrivilegeResult> CachedPrivileges;
|
||||
};
|
||||
|
||||
/** Per context cache, game will always exist but others may not */
|
||||
TMap<ECommonUserOnlineContext, FCachedData> CachedDataMap;
|
||||
|
||||
/** Looks up cached data using resolution rules */
|
||||
FCachedData* GetCachedData(ECommonUserOnlineContext Context);
|
||||
const FCachedData* GetCachedData(ECommonUserOnlineContext Context) const;
|
||||
|
||||
/** Updates cached privilege results, will propagate to game if needed */
|
||||
void UpdateCachedPrivilegeResult(ECommonUserPrivilege Privilege, ECommonUserPrivilegeResult Result, ECommonUserOnlineContext Context);
|
||||
|
||||
/** Updates cached privilege results, will propagate to game if needed */
|
||||
void UpdateCachedNetId(const FUniqueNetIdRepl& NewId, ECommonUserOnlineContext Context);
|
||||
|
||||
/** Return the subsystem this is owned by */
|
||||
class UCommonUserSubsystem* GetSubsystem() const;
|
||||
};
|
||||
|
||||
|
||||
/** Delegates when initialization processes succeed or fail */
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FiveParams(FCommonUserOnInitializeCompleteMulticast, const UCommonUserInfo*, UserInfo, bool, bSuccess, FText, Error, ECommonUserPrivilege, RequestedPrivilege, ECommonUserOnlineContext, OnlineContext);
|
||||
DECLARE_DYNAMIC_DELEGATE_FiveParams(FCommonUserOnInitializeComplete, const UCommonUserInfo*, UserInfo, bool, bSuccess, FText, Error, ECommonUserPrivilege, RequestedPrivilege, ECommonUserOnlineContext, OnlineContext);
|
||||
|
||||
/** Delegate when a system error message is sent, the game can choose to display it to the user using the type tag */
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FCommonUserHandleSystemMessageDelegate, FGameplayTag, MessageType, FText, TitleText, FText, BodyText);
|
||||
|
||||
/** Delegate when a privilege changes, this can be bound to see if online status/etc changes during gameplay */
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FCommonUserAvailabilityChangedDelegate, const UCommonUserInfo*, UserInfo, ECommonUserPrivilege, Privilege, ECommonUserAvailability, OldAvailability, ECommonUserAvailability, NewAvailability);
|
||||
|
||||
|
||||
/** Parameter struct for initialize functions, this would normally be filled in by wrapper functions like async nodes */
|
||||
USTRUCT(BlueprintType)
|
||||
struct COMMONUSER_API FCommonUserInitializeParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** What local player index to use, can specify one above current if can create player is enabled */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Default)
|
||||
int32 LocalPlayerIndex = 0;
|
||||
|
||||
/** Deprecated method of selecting platform user and input device */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Default)
|
||||
int32 ControllerId = -1;
|
||||
|
||||
/** Primary controller input device for this user, they could also have additional secondary devices */
|
||||
UPROPERTY(BlueprintReadOnly, Category = UserInfo)
|
||||
FInputDeviceId PrimaryInputDevice;
|
||||
|
||||
/** Specifies the logical user on the local platform */
|
||||
UPROPERTY(BlueprintReadOnly, Category = UserInfo)
|
||||
FPlatformUserId PlatformUser;
|
||||
|
||||
/** Generally either CanPlay or CanPlayOnline, specifies what level of privilege is required */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Default)
|
||||
ECommonUserPrivilege RequestedPrivilege = ECommonUserPrivilege::CanPlay;
|
||||
|
||||
/** What specific online context to log in to, game means to login to all relevant ones */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Default)
|
||||
ECommonUserOnlineContext OnlineContext = ECommonUserOnlineContext::Game;
|
||||
|
||||
/** True if this is allowed to create a new local player for initial login */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Default)
|
||||
bool bCanCreateNewLocalPlayer = false;
|
||||
|
||||
/** True if this player can be a guest user without an actual online presence */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Default)
|
||||
bool bCanUseGuestLogin = false;
|
||||
|
||||
/** True if we should not show login errors, the game will be responsible for displaying them */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Default)
|
||||
bool bSuppressLoginErrors = false;
|
||||
|
||||
/** If bound, call this dynamic delegate at completion of login */
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Default)
|
||||
FCommonUserOnInitializeComplete OnUserInitializeComplete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Game subsystem that handles queries and changes to user identity and login status.
|
||||
* One subsystem is created for each game instance and can be accessed from blueprints or C++ code.
|
||||
* If a game-specific subclass exists, this base subsystem will not be created.
|
||||
*/
|
||||
UCLASS(BlueprintType, Config=Engine)
|
||||
class COMMONUSER_API UCommonUserSubsystem : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UCommonUserSubsystem() { }
|
||||
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
virtual bool ShouldCreateSubsystem(UObject* Outer) const override;
|
||||
|
||||
|
||||
/** BP delegate called when any requested initialization request completes */
|
||||
UPROPERTY(BlueprintAssignable, Category = CommonUser)
|
||||
FCommonUserOnInitializeCompleteMulticast OnUserInitializeComplete;
|
||||
|
||||
/** BP delegate called when the system sends an error/warning message */
|
||||
UPROPERTY(BlueprintAssignable, Category = CommonUser)
|
||||
FCommonUserHandleSystemMessageDelegate OnHandleSystemMessage;
|
||||
|
||||
/** BP delegate called when privilege availability changes for a user */
|
||||
UPROPERTY(BlueprintAssignable, Category = CommonUser)
|
||||
FCommonUserAvailabilityChangedDelegate OnUserPrivilegeChanged;
|
||||
|
||||
/** Send a system message via OnHandleSystemMessage */
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser)
|
||||
virtual void SendSystemMessage(FGameplayTag MessageType, FText TitleText, FText BodyText);
|
||||
|
||||
/** Sets the maximum number of local players, will not destroy existing ones */
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser)
|
||||
virtual void SetMaxLocalPlayers(int32 InMaxLocalPLayers);
|
||||
|
||||
/** Gets the maximum number of local players */
|
||||
UFUNCTION(BlueprintPure, Category = CommonUser)
|
||||
int32 GetMaxLocalPlayers() const;
|
||||
|
||||
/** Gets the current number of local players, will always be at least 1 */
|
||||
UFUNCTION(BlueprintPure, Category = CommonUser)
|
||||
int32 GetNumLocalPlayers() const;
|
||||
|
||||
/** Returns the state of initializing the specified local player */
|
||||
UFUNCTION(BlueprintPure, Category = CommonUser)
|
||||
ECommonUserInitializationState GetLocalPlayerInitializationState(int32 LocalPlayerIndex) const;
|
||||
|
||||
/** Returns the user info for a given local player index in game instance, 0 is always valid in a running game */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure = False, Category = CommonUser)
|
||||
const UCommonUserInfo* GetUserInfoForLocalPlayerIndex(int32 LocalPlayerIndex) const;
|
||||
|
||||
/** Deprecated, use PlatformUserId when available */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure = False, Category = CommonUser)
|
||||
const UCommonUserInfo* GetUserInfoForPlatformUserIndex(int32 PlatformUserIndex) const;
|
||||
|
||||
/** Returns the primary user info for a given platform user index. Can return null */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure = False, Category = CommonUser)
|
||||
const UCommonUserInfo* GetUserInfoForPlatformUser(FPlatformUserId PlatformUser) const;
|
||||
|
||||
/** Returns the user info for a unique net id. Can return null */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure = False, Category = CommonUser)
|
||||
const UCommonUserInfo* GetUserInfoForUniqueNetId(const FUniqueNetIdRepl& NetId) const;
|
||||
|
||||
/** Deprecated, use InputDeviceId when available */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure = False, Category = CommonUser)
|
||||
const UCommonUserInfo* GetUserInfoForControllerId(int32 ControllerId) const;
|
||||
|
||||
/** Returns the user info for a given input device. Can return null */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure = False, Category = CommonUser)
|
||||
const UCommonUserInfo* GetUserInfoForInputDevice(FInputDeviceId InputDevice) const;
|
||||
|
||||
/**
|
||||
* Tries to start the process of creating or updating a local player, including logging in and creating a player controller.
|
||||
* When the process has succeeded or failed, it will broadcast the OnUserInitializeComplete delegate.
|
||||
*
|
||||
* @param LocalPlayerIndex Desired index of LocalPlayer in Game Instance, 0 will be primary player and 1+ for local multiplayer
|
||||
* @param PrimaryInputDevice The physical controller that should be mapped to this user, will use the default device if invalid
|
||||
* @param bCanUseGuestLogin If true, this player can be a guest without a real Unique Net Id
|
||||
*
|
||||
* @returns true if the process was started, false if it failed before properly starting
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser)
|
||||
virtual bool TryToInitializeForLocalPlay(int32 LocalPlayerIndex, FInputDeviceId PrimaryInputDevice, bool bCanUseGuestLogin);
|
||||
|
||||
/**
|
||||
* Starts the process of taking a locally logged in user and doing a full online login including account permission checks.
|
||||
* When the process has succeeded or failed, it will broadcast the OnUserInitializeComplete delegate.
|
||||
*
|
||||
* @param LocalPlayerIndex Index of existing LocalPlayer in Game Instance
|
||||
*
|
||||
* @returns true if the process was started, false if it failed before properly starting
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser)
|
||||
virtual bool TryToLoginForOnlinePlay(int32 LocalPlayerIndex);
|
||||
|
||||
/**
|
||||
* Starts a general user login and initialization process, using the params structure to determine what to log in to.
|
||||
* When the process has succeeded or failed, it will broadcast the OnUserInitializeComplete delegate.
|
||||
* AsyncAction_CommonUserInitialize provides several wrapper functions for using this in an Event graph.
|
||||
*
|
||||
* @returns true if the process was started, false if it failed before properly starting
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser)
|
||||
virtual bool TryToInitializeUser(FCommonUserInitializeParams Params);
|
||||
|
||||
/**
|
||||
* Starts the process of listening for user input for new and existing controllers and logging them.
|
||||
* This will insert a key input handler on the active GameViewportClient and is turned off by calling again with empty key arrays.
|
||||
*
|
||||
* @param AnyUserKeys Listen for these keys for any user, even the default user. Set this for an initial press start screen or empty to disable
|
||||
* @param NewUserKeys Listen for these keys for a new user without a player controller. Set this for splitscreen/local multiplayer or empty to disable
|
||||
* @param Params Params passed to TryToInitializeUser after detecting key input
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser)
|
||||
virtual void ListenForLoginKeyInput(TArray<FKey> AnyUserKeys, TArray<FKey> NewUserKeys, FCommonUserInitializeParams Params);
|
||||
|
||||
/** Attempts to cancel an in-progress initialization attempt, this may not work on all platforms but will disable callbacks */
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser)
|
||||
virtual bool CancelUserInitialization(int32 LocalPlayerIndex);
|
||||
|
||||
/** Logs a player out of any online systems, and optionally destroys the player entirely if it's not the first one */
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser)
|
||||
virtual bool TryToLogOutUser(int32 LocalPlayerIndex, bool bDestroyPlayer = false);
|
||||
|
||||
/** Resets the login and initialization state when returning to the main menu after an error */
|
||||
UFUNCTION(BlueprintCallable, Category = CommonUser)
|
||||
virtual void ResetUserState();
|
||||
|
||||
/** Returns true if this this could be a real platform user with a valid identity (even if not currently logged in) */
|
||||
virtual bool IsRealPlatformUserIndex(int32 PlatformUserIndex) const;
|
||||
|
||||
/** Returns true if this this could be a real platform user with a valid identity (even if not currently logged in) */
|
||||
virtual bool IsRealPlatformUser(FPlatformUserId PlatformUser) const;
|
||||
|
||||
/** Converts index to id */
|
||||
virtual FPlatformUserId GetPlatformUserIdForIndex(int32 PlatformUserIndex) const;
|
||||
|
||||
/** Converts id to index */
|
||||
virtual int32 GetPlatformUserIndexForId(FPlatformUserId PlatformUser) const;
|
||||
|
||||
/** Gets the user for an input device */
|
||||
virtual FPlatformUserId GetPlatformUserIdForInputDevice(FInputDeviceId InputDevice) const;
|
||||
|
||||
/** Gets a user's primary input device id */
|
||||
virtual FInputDeviceId GetPrimaryInputDeviceForPlatformUser(FPlatformUserId PlatformUser) const;
|
||||
|
||||
/** Call from game code to set the cached trait tags when platform state or options changes */
|
||||
virtual void SetTraitTags(const FGameplayTagContainer& InTags);
|
||||
|
||||
/** Gets the current tags that affect feature avialability */
|
||||
const FGameplayTagContainer& GetTraitTags() const { return CachedTraitTags; }
|
||||
|
||||
/** Checks if a specific platform/feature tag is enabled */
|
||||
UFUNCTION(BlueprintPure, Category=CommonUser)
|
||||
bool HasTraitTag(const FGameplayTag TraitTag) const { return CachedTraitTags.HasTag(TraitTag); }
|
||||
|
||||
/** Checks to see if we should display a press start/input confirmation screen at startup. Games can call this or check the trait tags directly */
|
||||
UFUNCTION(BlueprintPure, BlueprintPure, Category=CommonUser)
|
||||
virtual bool ShouldWaitForStartInput() const;
|
||||
|
||||
|
||||
// Functions for accessing low-level online system information
|
||||
|
||||
#if COMMONUSER_OSSV1
|
||||
/** Returns OSS interface of specific type, will return null if there is no type */
|
||||
IOnlineSubsystem* GetOnlineSubsystem(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Returns identity interface of specific type, will return null if there is no type */
|
||||
IOnlineIdentity* GetOnlineIdentity(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Returns human readable name of OSS system */
|
||||
FName GetOnlineSubsystemName(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Returns the current online connection status */
|
||||
EOnlineServerConnectionStatus::Type GetConnectionStatus(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
#else
|
||||
/** Get the services provider type, or None if there isn't one. */
|
||||
UE::Online::EOnlineServices GetOnlineServicesProvider(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Returns auth interface of specific type, will return null if there is no type */
|
||||
UE::Online::IAuthPtr GetOnlineAuth(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Returns the current online connection status */
|
||||
UE::Online::EOnlineServicesConnectionStatus GetConnectionStatus(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
#endif
|
||||
|
||||
/** Returns true if we are currently connected to backend servers */
|
||||
bool HasOnlineConnection(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Returns the current login status for a player on the specified online system, only works for real platform users */
|
||||
ELoginStatusType GetLocalUserLoginStatus(FPlatformUserId PlatformUser, ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Returns the unique net id for a local platform user */
|
||||
FUniqueNetIdRepl GetLocalUserNetId(FPlatformUserId PlatformUser, ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Returns the nickname for a local platform user, this is cached in common user Info */
|
||||
FString GetLocalUserNickname(FPlatformUserId PlatformUser, ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
|
||||
/** Convert a user id to a debug string */
|
||||
FString PlatformUserIdToString(FPlatformUserId UserId);
|
||||
|
||||
/** Convert a context to a debug string */
|
||||
FString ECommonUserOnlineContextToString(ECommonUserOnlineContext Context);
|
||||
|
||||
/** Returns human readable string for privilege checks */
|
||||
virtual FText GetPrivilegeDescription(ECommonUserPrivilege Privilege) const;
|
||||
virtual FText GetPrivilegeResultDescription(ECommonUserPrivilegeResult Result) const;
|
||||
|
||||
/**
|
||||
* Starts the process of login for an existing local user, will return false if callback was not scheduled
|
||||
* This activates the low level state machine and does not modify the initialization state on user info
|
||||
*/
|
||||
DECLARE_DELEGATE_FiveParams(FOnLocalUserLoginCompleteDelegate, const UCommonUserInfo* /*UserInfo*/, ELoginStatusType /*NewStatus*/, FUniqueNetIdRepl /*NetId*/, const TOptional<FOnlineErrorType>& /*Error*/, ECommonUserOnlineContext /*Type*/);
|
||||
virtual bool LoginLocalUser(const UCommonUserInfo* UserInfo, ECommonUserPrivilege RequestedPrivilege, ECommonUserOnlineContext Context, FOnLocalUserLoginCompleteDelegate OnComplete);
|
||||
|
||||
/** Assign a local player to a specific local user and call callbacks as needed */
|
||||
virtual void SetLocalPlayerUserInfo(ULocalPlayer* LocalPlayer, const UCommonUserInfo* UserInfo);
|
||||
|
||||
/** Resolves a context that has default behavior into a specific context */
|
||||
ECommonUserOnlineContext ResolveOnlineContext(ECommonUserOnlineContext Context) const;
|
||||
|
||||
/** True if there is a separate platform and service interface */
|
||||
bool HasSeparatePlatformContext() const;
|
||||
|
||||
protected:
|
||||
/** Internal structure that caches status and pointers for each online context */
|
||||
struct FOnlineContextCache
|
||||
{
|
||||
#if COMMONUSER_OSSV1
|
||||
/** Pointer to base subsystem, will stay valid as long as game instance does */
|
||||
IOnlineSubsystem* OnlineSubsystem = nullptr;
|
||||
|
||||
/** Cached identity system, this will always be valid */
|
||||
IOnlineIdentityPtr IdentityInterface;
|
||||
|
||||
/** Last connection status that was passed into the HandleNetworkConnectionStatusChanged hander */
|
||||
EOnlineServerConnectionStatus::Type CurrentConnectionStatus = EOnlineServerConnectionStatus::Normal;
|
||||
#else
|
||||
/** Online services, accessor to specific services */
|
||||
UE::Online::IOnlineServicesPtr OnlineServices;
|
||||
/** Cached auth service */
|
||||
UE::Online::IAuthPtr AuthService;
|
||||
/** Login status changed event handle */
|
||||
UE::Online::FOnlineEventDelegateHandle LoginStatusChangedHandle;
|
||||
/** Connection status changed event handle */
|
||||
UE::Online::FOnlineEventDelegateHandle ConnectionStatusChangedHandle;
|
||||
/** Last connection status that was passed into the HandleNetworkConnectionStatusChanged hander */
|
||||
UE::Online::EOnlineServicesConnectionStatus CurrentConnectionStatus = UE::Online::EOnlineServicesConnectionStatus::NotConnected;
|
||||
#endif
|
||||
|
||||
/** Resets state, important to clear all shared ptrs */
|
||||
void Reset()
|
||||
{
|
||||
#if COMMONUSER_OSSV1
|
||||
OnlineSubsystem = nullptr;
|
||||
IdentityInterface.Reset();
|
||||
CurrentConnectionStatus = EOnlineServerConnectionStatus::Normal;
|
||||
#else
|
||||
OnlineServices.Reset();
|
||||
AuthService.Reset();
|
||||
CurrentConnectionStatus = UE::Online::EOnlineServicesConnectionStatus::NotConnected;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/** Internal structure to represent an in-progress login request */
|
||||
struct FUserLoginRequest : public TSharedFromThis<FUserLoginRequest>
|
||||
{
|
||||
FUserLoginRequest(UCommonUserInfo* InUserInfo, ECommonUserPrivilege InPrivilege, ECommonUserOnlineContext InContext, FOnLocalUserLoginCompleteDelegate&& InDelegate)
|
||||
: UserInfo(TWeakObjectPtr<UCommonUserInfo>(InUserInfo))
|
||||
, DesiredPrivilege(InPrivilege)
|
||||
, DesiredContext(InContext)
|
||||
, Delegate(MoveTemp(InDelegate))
|
||||
{}
|
||||
|
||||
/** Which local user is trying to log on */
|
||||
TWeakObjectPtr<UCommonUserInfo> UserInfo;
|
||||
|
||||
/** Overall state of login request, could come from many sources */
|
||||
ECommonUserAsyncTaskState OverallLoginState = ECommonUserAsyncTaskState::NotStarted;
|
||||
|
||||
/** State of attempt to use platform auth. When started, this immediately transitions to Failed for OSSv1, as we do not support platform auth there. */
|
||||
ECommonUserAsyncTaskState TransferPlatformAuthState = ECommonUserAsyncTaskState::NotStarted;
|
||||
|
||||
/** State of attempt to use AutoLogin */
|
||||
ECommonUserAsyncTaskState AutoLoginState = ECommonUserAsyncTaskState::NotStarted;
|
||||
|
||||
/** State of attempt to use external login UI */
|
||||
ECommonUserAsyncTaskState LoginUIState = ECommonUserAsyncTaskState::NotStarted;
|
||||
|
||||
/** Final privilege to that is requested */
|
||||
ECommonUserPrivilege DesiredPrivilege = ECommonUserPrivilege::Invalid_Count;
|
||||
|
||||
/** State of attempt to request the relevant privilege */
|
||||
ECommonUserAsyncTaskState PrivilegeCheckState = ECommonUserAsyncTaskState::NotStarted;
|
||||
|
||||
/** The final context to log into */
|
||||
ECommonUserOnlineContext DesiredContext = ECommonUserOnlineContext::Invalid;
|
||||
|
||||
/** What online system we are currently logging into */
|
||||
ECommonUserOnlineContext CurrentContext = ECommonUserOnlineContext::Invalid;
|
||||
|
||||
/** User callback for completion */
|
||||
FOnLocalUserLoginCompleteDelegate Delegate;
|
||||
|
||||
/** Most recent/relevant error to display to user */
|
||||
TOptional<FOnlineErrorType> Error;
|
||||
};
|
||||
|
||||
|
||||
/** Create a new user info object */
|
||||
virtual UCommonUserInfo* CreateLocalUserInfo(int32 LocalPlayerIndex);
|
||||
|
||||
/** Deconst wrapper for const getters */
|
||||
FORCEINLINE UCommonUserInfo* ModifyInfo(const UCommonUserInfo* Info) { return const_cast<UCommonUserInfo*>(Info); }
|
||||
|
||||
/** Refresh user info from OSS */
|
||||
virtual void RefreshLocalUserInfo(UCommonUserInfo* UserInfo);
|
||||
|
||||
/** Possibly send privilege availability notification, compares current value to cached old value */
|
||||
virtual void HandleChangedAvailability(UCommonUserInfo* UserInfo, ECommonUserPrivilege Privilege, ECommonUserAvailability OldAvailability);
|
||||
|
||||
/** Updates the cached privilege on a user and notifies delegate */
|
||||
virtual void UpdateUserPrivilegeResult(UCommonUserInfo* UserInfo, ECommonUserPrivilege Privilege, ECommonUserPrivilegeResult Result, ECommonUserOnlineContext Context);
|
||||
|
||||
/** Gets internal data for a type of online system, can return null for service */
|
||||
const FOnlineContextCache* GetContextCache(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game) const;
|
||||
FOnlineContextCache* GetContextCache(ECommonUserOnlineContext Context = ECommonUserOnlineContext::Game);
|
||||
|
||||
/** Create and set up system objects before delegates are bound */
|
||||
virtual void CreateOnlineContexts();
|
||||
virtual void DestroyOnlineContexts();
|
||||
|
||||
/** Bind online delegates */
|
||||
virtual void BindOnlineDelegates();
|
||||
|
||||
/** Forcibly logs out and deinitializes a single user */
|
||||
virtual void LogOutLocalUser(FPlatformUserId PlatformUser);
|
||||
|
||||
/** Performs the next step of a login request, which could include completing it. Returns true if it's done */
|
||||
virtual void ProcessLoginRequest(TSharedRef<FUserLoginRequest> Request);
|
||||
|
||||
/** Call login on OSS, with platform auth from the platform OSS. Return true if AutoLogin started */
|
||||
virtual bool TransferPlatformAuth(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
|
||||
/** Call AutoLogin on OSS. Return true if AutoLogin started. */
|
||||
virtual bool AutoLogin(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
|
||||
/** Call ShowLoginUI on OSS. Return true if ShowLoginUI started. */
|
||||
virtual bool ShowLoginUI(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
|
||||
/** Call QueryUserPrivilege on OSS. Return true if QueryUserPrivilege started. */
|
||||
virtual bool QueryUserPrivilege(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
|
||||
/** OSS-specific functions */
|
||||
#if COMMONUSER_OSSV1
|
||||
virtual ECommonUserPrivilege ConvertOSSPrivilege(EUserPrivileges::Type Privilege) const;
|
||||
virtual EUserPrivileges::Type ConvertOSSPrivilege(ECommonUserPrivilege Privilege) const;
|
||||
virtual ECommonUserPrivilegeResult ConvertOSSPrivilegeResult(EUserPrivileges::Type Privilege, uint32 Results) const;
|
||||
|
||||
void BindOnlineDelegatesOSSv1();
|
||||
bool AutoLoginOSSv1(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
bool ShowLoginUIOSSv1(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
bool QueryUserPrivilegeOSSv1(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
#else
|
||||
virtual ECommonUserPrivilege ConvertOnlineServicesPrivilege(UE::Online::EUserPrivileges Privilege) const;
|
||||
virtual UE::Online::EUserPrivileges ConvertOnlineServicesPrivilege(ECommonUserPrivilege Privilege) const;
|
||||
virtual ECommonUserPrivilegeResult ConvertOnlineServicesPrivilegeResult(UE::Online::EUserPrivileges Privilege, UE::Online::EPrivilegeResults Results) const;
|
||||
|
||||
void BindOnlineDelegatesOSSv2();
|
||||
void CacheConnectionStatus(ECommonUserOnlineContext Context);
|
||||
bool TransferPlatformAuthOSSv2(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
bool AutoLoginOSSv2(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
bool ShowLoginUIOSSv2(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
bool QueryUserPrivilegeOSSv2(FOnlineContextCache* System, TSharedRef<FUserLoginRequest> Request, FPlatformUserId PlatformUser);
|
||||
TSharedPtr<UE::Online::FAccountInfo> GetOnlineServiceAccountInfo(UE::Online::IAuthPtr AuthService, FPlatformUserId InUserId) const;
|
||||
#endif
|
||||
|
||||
/** Callbacks for OSS functions */
|
||||
#if COMMONUSER_OSSV1
|
||||
virtual void HandleIdentityLoginStatusChanged(int32 PlatformUserIndex, ELoginStatus::Type OldStatus, ELoginStatus::Type NewStatus, const FUniqueNetId& NewId, ECommonUserOnlineContext Context);
|
||||
virtual void HandleUserLoginCompleted(int32 PlatformUserIndex, bool bWasSuccessful, const FUniqueNetId& NetId, const FString& Error, ECommonUserOnlineContext Context);
|
||||
virtual void HandleControllerPairingChanged(int32 PlatformUserIndex, FControllerPairingChangedUserInfo PreviousUser, FControllerPairingChangedUserInfo NewUser);
|
||||
virtual void HandleNetworkConnectionStatusChanged(const FString& ServiceName, EOnlineServerConnectionStatus::Type LastConnectionStatus, EOnlineServerConnectionStatus::Type ConnectionStatus, ECommonUserOnlineContext Context);
|
||||
virtual void HandleOnLoginUIClosed(TSharedPtr<const FUniqueNetId> LoggedInNetId, const int PlatformUserIndex, const FOnlineError& Error, ECommonUserOnlineContext Context);
|
||||
virtual void HandleCheckPrivilegesComplete(const FUniqueNetId& UserId, EUserPrivileges::Type Privilege, uint32 PrivilegeResults, ECommonUserPrivilege RequestedPrivilege, TWeakObjectPtr<UCommonUserInfo> CommonUserInfo, ECommonUserOnlineContext Context);
|
||||
#else
|
||||
virtual void HandleAuthLoginStatusChanged(const UE::Online::FAuthLoginStatusChanged& EventParameters, ECommonUserOnlineContext Context);
|
||||
virtual void HandleUserLoginCompletedV2(const UE::Online::TOnlineResult<UE::Online::FAuthLogin>& Result, FPlatformUserId PlatformUser, ECommonUserOnlineContext Context);
|
||||
virtual void HandleOnLoginUIClosedV2(const UE::Online::TOnlineResult<UE::Online::FExternalUIShowLoginUI>& Result, FPlatformUserId PlatformUser, ECommonUserOnlineContext Context);
|
||||
virtual void HandleNetworkConnectionStatusChanged(const UE::Online::FConnectionStatusChanged& EventParameters, ECommonUserOnlineContext Context);
|
||||
virtual void HandleCheckPrivilegesComplete(const UE::Online::TOnlineResult<UE::Online::FQueryUserPrivilege>& Result, TWeakObjectPtr<UCommonUserInfo> CommonUserInfo, UE::Online::EUserPrivileges DesiredPrivilege, ECommonUserOnlineContext Context);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Callback for when an input device (i.e. a gamepad) has been connected or disconnected.
|
||||
*/
|
||||
virtual void HandleInputDeviceConnectionChanged(EInputDeviceConnectionState NewConnectionState, FPlatformUserId PlatformUserId, FInputDeviceId InputDeviceId);
|
||||
|
||||
virtual void HandleLoginForUserInitialize(const UCommonUserInfo* UserInfo, ELoginStatusType NewStatus, FUniqueNetIdRepl NetId, const TOptional<FOnlineErrorType>& Error, ECommonUserOnlineContext Context, FCommonUserInitializeParams Params);
|
||||
virtual void HandleUserInitializeFailed(FCommonUserInitializeParams Params, FText Error);
|
||||
virtual void HandleUserInitializeSucceeded(FCommonUserInitializeParams Params);
|
||||
|
||||
/** Callback for handling press start/login logic */
|
||||
virtual bool OverrideInputKeyForLogin(FInputKeyEventArgs& EventArgs);
|
||||
|
||||
|
||||
/** Previous override handler, will restore on cancel */
|
||||
FOverrideInputKeyHandler WrappedInputKeyHandler;
|
||||
|
||||
/** List of keys to listen for from any user */
|
||||
TArray<FKey> LoginKeysForAnyUser;
|
||||
|
||||
/** List of keys to listen for a new unmapped user */
|
||||
TArray<FKey> LoginKeysForNewUser;
|
||||
|
||||
/** Params to use for a key-triggered login */
|
||||
FCommonUserInitializeParams ParamsForLoginKey;
|
||||
|
||||
/** Maximum number of local players */
|
||||
int32 MaxNumberOfLocalPlayers = 0;
|
||||
|
||||
/** True if this is a dedicated server, which doesn't require a LocalPlayer */
|
||||
bool bIsDedicatedServer = false;
|
||||
|
||||
/** List of current in progress login requests */
|
||||
TArray<TSharedRef<FUserLoginRequest>> ActiveLoginRequests;
|
||||
|
||||
/** Information about each local user, from local player index to user */
|
||||
UPROPERTY()
|
||||
TMap<int32, TObjectPtr<UCommonUserInfo>> LocalUserInfos;
|
||||
|
||||
/** Cached platform/mode trait tags */
|
||||
FGameplayTagContainer CachedTraitTags;
|
||||
|
||||
/** Do not access this outside of initialization */
|
||||
FOnlineContextCache* DefaultContextInternal = nullptr;
|
||||
FOnlineContextCache* ServiceContextInternal = nullptr;
|
||||
FOnlineContextCache* PlatformContextInternal = nullptr;
|
||||
|
||||
friend UCommonUserInfo;
|
||||
};
|
218
Plugins/CommonUser/Source/CommonUser/Public/CommonUserTypes.h
Normal file
218
Plugins/CommonUser/Source/CommonUser/Public/CommonUserTypes.h
Normal file
@ -0,0 +1,218 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#if COMMONUSER_OSSV1
|
||||
|
||||
// Online Subsystem (OSS v1) includes and forward declares
|
||||
#include "OnlineSubsystemTypes.h"
|
||||
class IOnlineSubsystem;
|
||||
struct FOnlineError;
|
||||
using FOnlineErrorType = FOnlineError;
|
||||
using ELoginStatusType = ELoginStatus::Type;
|
||||
|
||||
#else
|
||||
|
||||
// Online Services (OSS v2) includes and forward declares
|
||||
#include "Online/Connectivity.h"
|
||||
#include "Online/OnlineError.h"
|
||||
namespace UE::Online
|
||||
{
|
||||
enum class ELoginStatus : uint8;
|
||||
enum class EPrivilegeResults : uint32;
|
||||
enum class EUserPrivileges : uint8;
|
||||
using IAuthPtr = TSharedPtr<class IAuth>;
|
||||
using IOnlineServicesPtr = TSharedPtr<class IOnlineServices>;
|
||||
template <typename OpType>
|
||||
class TOnlineResult;
|
||||
struct FAuthLogin;
|
||||
struct FConnectionStatusChanged;
|
||||
struct FExternalUIShowLoginUI;
|
||||
struct FAuthLoginStatusChanged;
|
||||
struct FQueryUserPrivilege;
|
||||
struct FAccountInfo;
|
||||
}
|
||||
using FOnlineErrorType = UE::Online::FOnlineError;
|
||||
using ELoginStatusType = UE::Online::ELoginStatus;
|
||||
|
||||
#endif
|
||||
|
||||
#include "CommonUserTypes.generated.h"
|
||||
|
||||
|
||||
/** Enum specifying where and how to run online queries */
|
||||
UENUM(BlueprintType)
|
||||
enum class ECommonUserOnlineContext : uint8
|
||||
{
|
||||
/** Called from game code, this uses the default system but with special handling that could merge results from multiple contexts */
|
||||
Game,
|
||||
|
||||
/** The default engine online system, this will always exist and will be the same as either Service or Platform */
|
||||
Default,
|
||||
|
||||
/** Explicitly ask for the external service, which may not exist */
|
||||
Service,
|
||||
|
||||
/** Looks for external service first, then falls back to default */
|
||||
ServiceOrDefault,
|
||||
|
||||
/** Explicitly ask for the platform system, which may not exist */
|
||||
Platform,
|
||||
|
||||
/** Looks for platform system first, then falls back to default */
|
||||
PlatformOrDefault,
|
||||
|
||||
/** Invalid system */
|
||||
Invalid
|
||||
};
|
||||
|
||||
/** Enum describing the state of initialization for a specific user */
|
||||
UENUM(BlueprintType)
|
||||
enum class ECommonUserInitializationState : uint8
|
||||
{
|
||||
/** User has not started login process */
|
||||
Unknown,
|
||||
|
||||
/** Player is in the process of acquiring a user id with local login */
|
||||
DoingInitialLogin,
|
||||
|
||||
/** Player is performing the network login, they have already logged in locally */
|
||||
DoingNetworkLogin,
|
||||
|
||||
/** Player failed to log in at all */
|
||||
FailedtoLogin,
|
||||
|
||||
|
||||
/** Player is logged in and has access to online functionality */
|
||||
LoggedInOnline,
|
||||
|
||||
/** Player is logged in locally (either guest or real user), but cannot perform online actions */
|
||||
LoggedInLocalOnly,
|
||||
|
||||
|
||||
/** Invalid state or user */
|
||||
Invalid,
|
||||
};
|
||||
|
||||
/** Enum specifying different privileges and capabilities available to a user */
|
||||
UENUM(BlueprintType)
|
||||
enum class ECommonUserPrivilege : uint8
|
||||
{
|
||||
/** Whether the user can play at all, online or offline */
|
||||
CanPlay,
|
||||
|
||||
/** Whether the user can play in online modes */
|
||||
CanPlayOnline,
|
||||
|
||||
/** Whether the user can use text chat */
|
||||
CanCommunicateViaTextOnline,
|
||||
|
||||
/** Whether the user can use voice chat */
|
||||
CanCommunicateViaVoiceOnline,
|
||||
|
||||
/** Whether the user can access content generated by other users */
|
||||
CanUseUserGeneratedContent,
|
||||
|
||||
/** Whether the user can ever participate in cross-play */
|
||||
CanUseCrossPlay,
|
||||
|
||||
/** Invalid privilege (also the count of valid ones) */
|
||||
Invalid_Count UMETA(Hidden)
|
||||
};
|
||||
|
||||
/** Enum specifying the general availability of a feature or privilege, this combines information from multiple sources */
|
||||
UENUM(BlueprintType)
|
||||
enum class ECommonUserAvailability : uint8
|
||||
{
|
||||
/** State is completely unknown and needs to be queried */
|
||||
Unknown,
|
||||
|
||||
/** This feature is fully available for use right now */
|
||||
NowAvailable,
|
||||
|
||||
/** This might be available after the completion of normal login procedures */
|
||||
PossiblyAvailable,
|
||||
|
||||
/** This feature is not available now because of something like network connectivity but may be available in the future */
|
||||
CurrentlyUnavailable,
|
||||
|
||||
/** This feature will never be available for the rest of this session due to hard account or platform restrictions */
|
||||
AlwaysUnavailable,
|
||||
|
||||
/** Invalid feature */
|
||||
Invalid,
|
||||
};
|
||||
|
||||
/** Enum giving specific reasons why a user may or may not use a certain privilege */
|
||||
UENUM(BlueprintType)
|
||||
enum class ECommonUserPrivilegeResult : uint8
|
||||
{
|
||||
/** State is unknown and needs to be queried */
|
||||
Unknown,
|
||||
|
||||
/** This privilege is fully available for use */
|
||||
Available,
|
||||
|
||||
/** User has not fully logged in */
|
||||
UserNotLoggedIn,
|
||||
|
||||
/** User does not own the game or content */
|
||||
LicenseInvalid,
|
||||
|
||||
/** The game needs to be updated or patched before this will be available */
|
||||
VersionOutdated,
|
||||
|
||||
/** No network connection, this may be resolved by reconnecting */
|
||||
NetworkConnectionUnavailable,
|
||||
|
||||
/** Parental control failure */
|
||||
AgeRestricted,
|
||||
|
||||
/** Account does not have a required subscription or account type */
|
||||
AccountTypeRestricted,
|
||||
|
||||
/** Another account/user restriction such as being banned by the service */
|
||||
AccountUseRestricted,
|
||||
|
||||
/** Other platform-specific failure */
|
||||
PlatformFailure,
|
||||
};
|
||||
|
||||
/** Used to track the progress of different asynchronous operations */
|
||||
enum class ECommonUserAsyncTaskState : uint8
|
||||
{
|
||||
/** The task has not been started */
|
||||
NotStarted,
|
||||
/** The task is currently being processed */
|
||||
InProgress,
|
||||
/** The task has completed successfully */
|
||||
Done,
|
||||
/** The task failed to complete */
|
||||
Failed
|
||||
};
|
||||
|
||||
/** Detailed information about the online error. Effectively a wrapper for FOnlineError. */
|
||||
USTRUCT(BlueprintType)
|
||||
struct FOnlineResultInformation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Whether the operation was successful or not. If it was successful, the error fields of this struct will not contain extra information. */
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
bool bWasSuccessful = true;
|
||||
|
||||
/** The unique error id. Can be used to compare against specific handled errors. */
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
FString ErrorId;
|
||||
|
||||
/** Error text to display to the user. */
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
FText ErrorText;
|
||||
|
||||
/**
|
||||
* Initialize this from an FOnlineErrorType
|
||||
* @param InOnlineError the online error to initialize from
|
||||
*/
|
||||
void COMMONUSER_API FromOnlineError(const FOnlineErrorType& InOnlineError);
|
||||
};
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "GameFeatureAction.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSExperienceActionSet)
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSExperienceActionSetDataAsset)
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OLSSystem"
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "Misc/DataValidation.h"
|
||||
#endif
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSExperienceDefinitionPrimaryDataAsset)
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSExperienceDefinitionDataAsset)
|
||||
|
||||
#define LOCTEXT_NAMESPACE "OLSSystem"
|
||||
|
||||
|
@ -165,7 +165,7 @@ bool UOLSExperienceManagerComponent::IsExperienceLoaded() const
|
||||
}
|
||||
|
||||
void UOLSExperienceManagerComponent::CallOrRegister_OnExperienceLoaded_HighPriority(
|
||||
FOnOLSExperienceLoaded::FDelegate&& delegate)
|
||||
FOLSExperienceLoadedNativeDelegate::FDelegate&& delegate)
|
||||
{
|
||||
if (IsExperienceLoaded())
|
||||
{
|
||||
@ -177,7 +177,7 @@ void UOLSExperienceManagerComponent::CallOrRegister_OnExperienceLoaded_HighPrior
|
||||
}
|
||||
}
|
||||
|
||||
void UOLSExperienceManagerComponent::CallOrRegister_OnExperienceLoaded(FOnOLSExperienceLoaded::FDelegate&& delegate)
|
||||
void UOLSExperienceManagerComponent::CallOrRegister_OnExperienceLoaded(FOLSExperienceLoadedNativeDelegate::FDelegate&& delegate)
|
||||
{
|
||||
if (IsExperienceLoaded())
|
||||
{
|
||||
@ -190,7 +190,7 @@ void UOLSExperienceManagerComponent::CallOrRegister_OnExperienceLoaded(FOnOLSExp
|
||||
}
|
||||
|
||||
void UOLSExperienceManagerComponent::CallOrRegister_OnExperienceLoaded_LowPriority(
|
||||
FOnOLSExperienceLoaded::FDelegate&& delegate)
|
||||
FOLSExperienceLoadedNativeDelegate::FDelegate&& delegate)
|
||||
{
|
||||
if (IsExperienceLoaded())
|
||||
{
|
||||
|
529
Source/ols/Private/GameModes/OLSGameMode.cpp
Normal file
529
Source/ols/Private/GameModes/OLSGameMode.cpp
Normal file
@ -0,0 +1,529 @@
|
||||
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
||||
|
||||
|
||||
#include "GameModes/OLSGameMode.h"
|
||||
|
||||
#include "CommonUserSubsystem.h"
|
||||
#include "GameMapsSettings.h"
|
||||
#include "DataAssets/OLSExperienceDefinitionDataAsset.h"
|
||||
#include "DataAssets/OLSPawnDataAsset.h"
|
||||
#include "GameModes/OLSExperienceManagerComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Player/OLSPlayerState.h"
|
||||
#include "Systems/OLSAssetManager.h"
|
||||
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSGameMode)
|
||||
|
||||
// AOLSGameMode::AOLSGameMode(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
||||
// {
|
||||
// // @TODO: Implement this.
|
||||
// // GameStateClass = ALyraGameState::StaticClass();
|
||||
// // GameSessionClass = ALyraGameSession::StaticClass();
|
||||
// // PlayerControllerClass = ALyraPlayerController::StaticClass();
|
||||
// // ReplaySpectatorPlayerControllerClass = ALyraReplayPlayerController::StaticClass();
|
||||
// // PlayerStateClass = ALyraPlayerState::StaticClass();
|
||||
// // DefaultPawnClass = ALyraCharacter::StaticClass();
|
||||
// // HUDClass = ALyraHUD::StaticClass();
|
||||
// }
|
||||
|
||||
const UOLSPawnDataAsset* AOLSGameMode::GetPawnDataForController(const AController* controller) const
|
||||
{
|
||||
// See if pawn data is already set on the player state
|
||||
if (controller)
|
||||
{
|
||||
if (const AOLSPlayerState* playerState = controller->GetPlayerState<AOLSPlayerState>())
|
||||
{
|
||||
if (const UOLSPawnDataAsset* pawnData = playerState->GetPawnData<UOLSPawnDataAsset>())
|
||||
{
|
||||
return pawnData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not, fall back to the the default for the current experience
|
||||
check(GameState);
|
||||
UOLSExperienceManagerComponent* experienceComponent = GameState->FindComponentByClass<UOLSExperienceManagerComponent>();
|
||||
check(experienceComponent);
|
||||
|
||||
if (experienceComponent->IsExperienceLoaded())
|
||||
{
|
||||
const UOLSExperienceDefinitionDataAsset* experienceDataAsset = experienceComponent->GetCurrentExperienceChecked();
|
||||
if (experienceDataAsset->DefaultPawnData)
|
||||
{
|
||||
return experienceDataAsset->DefaultPawnData;
|
||||
}
|
||||
|
||||
// Experience is loaded and there's still no pawn data, fall back to the default for now
|
||||
return UOLSAssetManager::Get().GetDefaultPawnData();
|
||||
}
|
||||
|
||||
// Experience not loaded yet, so there is no pawn data to be had
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AOLSGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
|
||||
{
|
||||
Super::InitGame(MapName, Options, ErrorMessage);
|
||||
|
||||
// Wait for the next frame to give time to initialize startup settings
|
||||
GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ThisClass::HandleMatchAssignmentIfNotExpectingOne);
|
||||
}
|
||||
|
||||
UClass* AOLSGameMode::GetDefaultPawnClassForController_Implementation(AController* controller)
|
||||
{
|
||||
if (const UOLSPawnDataAsset* pawnData = GetPawnDataForController(controller))
|
||||
{
|
||||
if (pawnData->PawnClass)
|
||||
{
|
||||
return pawnData->PawnClass;
|
||||
}
|
||||
}
|
||||
|
||||
return Super::GetDefaultPawnClassForController_Implementation(controller);
|
||||
}
|
||||
|
||||
APawn* AOLSGameMode::SpawnDefaultPawnAtTransform_Implementation(
|
||||
AController* newPlayer,
|
||||
const FTransform& spawnTransform)
|
||||
{
|
||||
FActorSpawnParameters spawnInfo;
|
||||
spawnInfo.Instigator = GetInstigator();
|
||||
spawnInfo.ObjectFlags |= RF_Transient; // Never save the default player pawns into a map.
|
||||
spawnInfo.bDeferConstruction = true;
|
||||
|
||||
if (UClass* pawnClass = GetDefaultPawnClassForController(newPlayer))
|
||||
{
|
||||
if (APawn* spawnedPawn = GetWorld()->SpawnActor<APawn>(pawnClass, spawnTransform, spawnInfo))
|
||||
{
|
||||
// @TODO: Implement this as well as implement UOLSawnExtensionComponent
|
||||
// if (UOLSawnExtensionComponent* PawnExtComp = UOLSawnExtensionComponent::FindPawnExtensionComponent(spawnedPawn))
|
||||
// {
|
||||
// if (const ULyraPawnData* PawnData = GetPawnDataForController(newPlayer))
|
||||
// {
|
||||
// PawnExtComp->SetPawnData(PawnData);
|
||||
// }
|
||||
// else
|
||||
// { // @TODO: Replace this with out custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("Game mode was unable to set PawnData on the spawned pawn [%s]."), *GetNameSafe(spawnedPawn }
|
||||
// }
|
||||
|
||||
spawnedPawn->FinishSpawning(spawnTransform);
|
||||
|
||||
return spawnedPawn;
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO: Replace this with out custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("Game mode was unable to spawn Pawn of class [%s] at [%s]."), *GetNameSafe(pawnClass), *spawnTransform.ToHumanReadableString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO: Replace this with out custom log.
|
||||
// UE_LOG(LogLyra, Error, TEXT("Game mode was unable to spawn Pawn due to NULL pawn class."));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AOLSGameMode::ShouldSpawnAtStartSpot(AController* Player)
|
||||
{
|
||||
// We never want to use the start spot, always use the spawn management component.
|
||||
return false;
|
||||
}
|
||||
|
||||
void AOLSGameMode::HandleStartingNewPlayer_Implementation(APlayerController* newPlayer)
|
||||
{
|
||||
// Delay starting new players until the experience has been loaded
|
||||
// (players who log in prior to that will be started by OnExperienceLoaded)
|
||||
if (IsExperienceLoaded())
|
||||
{
|
||||
Super::HandleStartingNewPlayer_Implementation(newPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
AActor* AOLSGameMode::ChoosePlayerStart_Implementation(AController* player)
|
||||
{
|
||||
// @TODO: Implement this as well as implement UOLSPlayerSpawningManagerComponent
|
||||
// if (UOLSPlayerSpawningManagerComponent* PlayerSpawningComponent = GameState->FindComponentByClass<ULyraPlayerSpawningManagerComponent>())
|
||||
// {
|
||||
// return PlayerSpawningComponent->ChoosePlayerStart(player);
|
||||
// }
|
||||
|
||||
return Super::ChoosePlayerStart_Implementation(player);
|
||||
}
|
||||
|
||||
void AOLSGameMode::FinishRestartPlayer(AController* newPlayer, const FRotator& startRotation)
|
||||
{
|
||||
// @TODO: Implement this as well as implement UOLSPlayerSpawningManagerComponent
|
||||
// if (UOLSPlayerSpawningManagerComponent* PlayerSpawningComponent = GameState->FindComponentByClass<UOLSPlayerSpawningManagerComponent>())
|
||||
// {
|
||||
// PlayerSpawningComponent->FinishRestartPlayer(NewPlayer, StartRotation);
|
||||
// }
|
||||
|
||||
Super::FinishRestartPlayer(newPlayer, startRotation);
|
||||
}
|
||||
|
||||
bool AOLSGameMode::PlayerCanRestart_Implementation(APlayerController* player)
|
||||
{
|
||||
return ControllerCanRestart(player);
|
||||
}
|
||||
|
||||
void AOLSGameMode::InitGameState()
|
||||
{
|
||||
Super::InitGameState();
|
||||
|
||||
// Listen for the experience load to complete
|
||||
UOLSExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<UOLSExperienceManagerComponent>();
|
||||
check(ExperienceComponent);
|
||||
ExperienceComponent->CallOrRegister_OnExperienceLoaded(FOLSExperienceLoadedNativeDelegate::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded));
|
||||
|
||||
}
|
||||
|
||||
bool AOLSGameMode::UpdatePlayerStartSpot(AController* player, const FString& portal, FString& outErrorMessage)
|
||||
{
|
||||
// Do nothing, we'll wait until PostLogin when we try to spawn the player for real.
|
||||
// Doing anything right now is no good, systems like team assignment haven't even occurred yet.
|
||||
return true;
|
||||
}
|
||||
|
||||
void AOLSGameMode::GenericPlayerInitialization(AController* newPlayer)
|
||||
{
|
||||
Super::GenericPlayerInitialization(newPlayer);
|
||||
|
||||
OnGameModePlayerInitialized.Broadcast(this, newPlayer);
|
||||
}
|
||||
|
||||
void AOLSGameMode::FailedToRestartPlayer(AController* newPlayer)
|
||||
{
|
||||
Super::FailedToRestartPlayer(newPlayer);
|
||||
|
||||
// If we tried to spawn a pawn and it failed, lets try again *note* check if there's actually a pawn class
|
||||
// before we try this forever.
|
||||
if (UClass* PawnClass = GetDefaultPawnClassForController(newPlayer))
|
||||
{
|
||||
if (APlayerController* newPC = Cast<APlayerController>(newPlayer))
|
||||
{
|
||||
// If it's a player don't loop forever, maybe something changed and they can no longer restart if so stop trying.
|
||||
if (PlayerCanRestart(newPC))
|
||||
{
|
||||
RequestPlayerRestartNextFrame(newPlayer, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO: Replace this with our custom log.
|
||||
// UE_LOG(LogLyra, Verbose, TEXT("FailedToRestartPlayer(%s) and PlayerCanRestart returned false, so we're not going to try again."), *GetPathNameSafe(newPlayer));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RequestPlayerRestartNextFrame(newPlayer, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO: Replace this with our custom log.
|
||||
// UE_LOG(LogLyra, Verbose, TEXT("FailedToRestartPlayer(%s) but there's no pawn class so giving up."), *GetPathNameSafe(newPlayer));
|
||||
}
|
||||
}
|
||||
|
||||
void AOLSGameMode::RequestPlayerRestartNextFrame(AController* controller, bool shouldForceReset)
|
||||
{
|
||||
if (shouldForceReset && controller)
|
||||
{
|
||||
controller->Reset();
|
||||
}
|
||||
|
||||
if (APlayerController* playerController = Cast<APlayerController>(controller))
|
||||
{
|
||||
GetWorldTimerManager().SetTimerForNextTick(playerController, &APlayerController::ServerRestartPlayer_Implementation);
|
||||
}
|
||||
// else if (AOLSPlayerBotController* BotController = Cast<ALyraPlayerBotController>(controller))
|
||||
// {
|
||||
// GetWorldTimerManager().SetTimerForNextTick(BotController, &ALyraPlayerBotController::ServerRestartController);
|
||||
// }
|
||||
}
|
||||
|
||||
bool AOLSGameMode::ControllerCanRestart(AController* controller)
|
||||
{
|
||||
if (APlayerController* playerController = Cast<APlayerController>(controller))
|
||||
{
|
||||
if (!Super::PlayerCanRestart_Implementation(playerController))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bot version of Super::PlayerCanRestart_Implementation
|
||||
if ((controller == nullptr) || controller->IsPendingKillPending())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO: Implement this after having PlayerSpawningManagerComponent
|
||||
// if (ULyraPlayerSpawningManagerComponent* PlayerSpawningComponent = GameState->FindComponentByClass<ULyraPlayerSpawningManagerComponent>())
|
||||
// {
|
||||
// return PlayerSpawningComponent->ControllerCanRestart(controller);
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AOLSGameMode::OnExperienceLoaded(const UOLSExperienceDefinitionDataAsset* currentExperience)
|
||||
{
|
||||
// Spawn any players that are already attached
|
||||
//@TODO: Here we're handling only *player* controllers, but in GetDefaultPawnClassForController_Implementation we skipped all controllers
|
||||
// GetDefaultPawnClassForController_Implementation might only be getting called for players anyways
|
||||
for (FConstPlayerControllerIterator iterator = GetWorld()->GetPlayerControllerIterator(); iterator; ++iterator)
|
||||
{
|
||||
APlayerController* playerController = Cast<APlayerController>(*iterator);
|
||||
if (playerController && playerController->GetPawn())
|
||||
{
|
||||
if (PlayerCanRestart(playerController))
|
||||
{
|
||||
RestartPlayer(playerController);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AOLSGameMode::IsExperienceLoaded() const
|
||||
{
|
||||
check(GameState);
|
||||
UOLSExperienceManagerComponent* experienceComponent = GameState->FindComponentByClass<
|
||||
UOLSExperienceManagerComponent>();
|
||||
check(experienceComponent);
|
||||
|
||||
return experienceComponent->IsExperienceLoaded();
|
||||
}
|
||||
|
||||
void AOLSGameMode::OnMatchAssignmentGiven(FPrimaryAssetId experienceId, const FString& experienceIdSource)
|
||||
{
|
||||
if (experienceId.IsValid())
|
||||
{
|
||||
// @TODO: Replace this with our custom.
|
||||
// UE_LOG(LogLyraExperience, Log, TEXT("Identified experience %s (Source: %s)"), *ExperienceId.ToString(), *ExperienceIdSource);
|
||||
|
||||
UOLSExperienceManagerComponent* experienceComponent = GameState->FindComponentByClass<UOLSExperienceManagerComponent>();
|
||||
check(experienceComponent);
|
||||
experienceComponent->SetCurrentExperience(experienceId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO: Replace this with our custom.
|
||||
// UE_LOG(LogLyraExperience, Error, TEXT("Failed to identify experience, loading screen will stay up forever"));
|
||||
}
|
||||
}
|
||||
|
||||
void AOLSGameMode::HandleMatchAssignmentIfNotExpectingOne()
|
||||
{
|
||||
FPrimaryAssetId experienceId;
|
||||
FString experienceIdSource;
|
||||
|
||||
// Precedence order (highest wins)
|
||||
// - Matchmaking assignment (if present)
|
||||
// - URL Options override
|
||||
// - Developer Settings (PIE only)
|
||||
// - Command Line override
|
||||
// - World Settings
|
||||
// - Dedicated server
|
||||
// - Default experience
|
||||
|
||||
UWorld* World = GetWorld();
|
||||
|
||||
if (!experienceId.IsValid() && UGameplayStatics::HasOption(OptionsString, TEXT("Experience")))
|
||||
{
|
||||
const FString ExperienceFromOptions = UGameplayStatics::ParseOption(OptionsString, TEXT("Experience"));
|
||||
experienceId = FPrimaryAssetId(FPrimaryAssetType(UOLSExperienceDefinitionDataAsset::StaticClass()->GetFName()), FName(*ExperienceFromOptions));
|
||||
experienceIdSource = TEXT("OptionsString");
|
||||
}
|
||||
|
||||
if (!experienceId.IsValid() && World->IsPlayInEditor())
|
||||
{
|
||||
// @TODO: Implement this when UOLSDeveloperSettings is implemented.
|
||||
// experienceId = GetDefault<UOLSDeveloperSettings>()->ExperienceOverride;
|
||||
experienceIdSource = TEXT("DeveloperSettings");
|
||||
}
|
||||
|
||||
// see if the command line wants to set the experience
|
||||
if (!experienceId.IsValid())
|
||||
{
|
||||
FString ExperienceFromCommandLine;
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("Experience="), ExperienceFromCommandLine))
|
||||
{
|
||||
experienceId = FPrimaryAssetId::ParseTypeAndName(ExperienceFromCommandLine);
|
||||
if (!experienceId.PrimaryAssetType.IsValid())
|
||||
{
|
||||
experienceId = FPrimaryAssetId(FPrimaryAssetType(UOLSExperienceDefinitionDataAsset::StaticClass()->GetFName()), FName(*ExperienceFromCommandLine));
|
||||
}
|
||||
experienceIdSource = TEXT("CommandLine");
|
||||
}
|
||||
}
|
||||
|
||||
// see if the world settings has a default experience
|
||||
if (!experienceId.IsValid())
|
||||
{
|
||||
// @TODO: Implement this when AOLSWorldSettings is implemented.
|
||||
// if (AOLSWorldSettings* TypedWorldSettings = Cast<ALyraWorldSettings>(GetWorldSettings()))
|
||||
// {
|
||||
// experienceId = TypedWorldSettings->GetDefaultGameplayExperience();
|
||||
// experienceIdSource = TEXT("WorldSettings");
|
||||
// }
|
||||
}
|
||||
|
||||
UOLSAssetManager& assetManager = UOLSAssetManager::Get();
|
||||
FAssetData dummy;
|
||||
if (experienceId.IsValid() && !assetManager.GetPrimaryAssetData(experienceId, /*out*/ dummy))
|
||||
{
|
||||
// @TODO: Replace this with our custom.
|
||||
// UE_LOG(LogLyraExperience, Error, TEXT("EXPERIENCE: Wanted to use %s but couldn't find it, falling back to the default)"), *experienceIding());
|
||||
experienceId = FPrimaryAssetId();
|
||||
}
|
||||
|
||||
// Final fallback to the default experience
|
||||
if (!experienceId.IsValid())
|
||||
{
|
||||
if (TryDedicatedServerLogin())
|
||||
{
|
||||
// This will start to host as a dedicated server
|
||||
return;
|
||||
}
|
||||
|
||||
//@TODO: Pull this from a config setting or something
|
||||
experienceId = FPrimaryAssetId(FPrimaryAssetType("LyraExperienceDefinition"), FName("B_LyraDefaultExperience"));
|
||||
experienceIdSource = TEXT("Default");
|
||||
}
|
||||
|
||||
OnMatchAssignmentGiven(experienceId, experienceIdSource);
|
||||
}
|
||||
|
||||
bool AOLSGameMode::TryDedicatedServerLogin()
|
||||
{
|
||||
// Some basic code to register as an active dedicated server, this would be heavily modified by the game
|
||||
// FString defaultMap = UGameMapsSettings::GetGameDefaultMap();
|
||||
// UWorld* world = GetWorld();
|
||||
// UGameInstance* gameInstance = GetGameInstance();
|
||||
// if (gameInstance && world && world->GetNetMode() == NM_DedicatedServer && world->URL.Map == defaultMap)
|
||||
// {
|
||||
// // Only register if this is the default map on a dedicated server
|
||||
// UCommonUserSubsystem* userSubsystem = gameInstance->GetSubsystem<UCommonUserSubsystem>();
|
||||
//
|
||||
// // Dedicated servers may need to do an online login
|
||||
// userSubsystem->OnUserInitializeComplete.AddDynamic(this, &ThisClass::OnUserInitializedForDedicatedServer);
|
||||
//
|
||||
// // There are no local users on dedicated server, but index 0 means the default platform user which is handled by the online login code
|
||||
// if (!userSubsystem->TryToLoginForOnlinePlay(0))
|
||||
// {
|
||||
// OnUserInitializedForDedicatedServer(nullptr, false, FText(), ECommonUserPrivilege::CanPlayOnline, ECommonUserOnlineContext::Default);
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AOLSGameMode::HostDedicatedServerMatch(ECommonSessionOnlineMode onlineMode)
|
||||
{
|
||||
// FPrimaryAssetType UserExperienceType = ULyraUserFacingExperienceDefinition::StaticClass()->GetFName();
|
||||
//
|
||||
// // Figure out what UserFacingExperience to load
|
||||
// FPrimaryAssetId UserExperienceId;
|
||||
// FString UserExperienceFromCommandLine;
|
||||
// if (FParse::Value(FCommandLine::Get(), TEXT("UserExperience="), UserExperienceFromCommandLine) ||
|
||||
// FParse::Value(FCommandLine::Get(), TEXT("Playlist="), UserExperienceFromCommandLine))
|
||||
// {
|
||||
// UserExperienceId = FPrimaryAssetId::ParseTypeAndName(UserExperienceFromCommandLine);
|
||||
// if (!UserExperienceId.PrimaryAssetType.IsValid())
|
||||
// {
|
||||
// UserExperienceId = FPrimaryAssetId(FPrimaryAssetType(UserExperienceType), FName(*UserExperienceFromCommandLine));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Search for the matching experience, it's fine to force load them because we're in dedicated server startup
|
||||
// ULyraAssetManager& AssetManager = ULyraAssetManager::Get();
|
||||
// TSharedPtr<FStreamableHandle> Handle = AssetManager.LoadPrimaryAssetsWithType(UserExperienceType);
|
||||
// if (ensure(Handle.IsValid()))
|
||||
// {
|
||||
// Handle->WaitUntilComplete();
|
||||
// }
|
||||
//
|
||||
// TArray<UObject*> UserExperiences;
|
||||
// AssetManager.GetPrimaryAssetObjectList(UserExperienceType, UserExperiences);
|
||||
// ULyraUserFacingExperienceDefinition* FoundExperience = nullptr;
|
||||
// ULyraUserFacingExperienceDefinition* DefaultExperience = nullptr;
|
||||
//
|
||||
// for (UObject* Object : UserExperiences)
|
||||
// {
|
||||
// ULyraUserFacingExperienceDefinition* UserExperience = Cast<ULyraUserFacingExperienceDefinition>(Object);
|
||||
// if (ensure(UserExperience))
|
||||
// {
|
||||
// if (UserExperience->GetPrimaryAssetId() == UserExperienceId)
|
||||
// {
|
||||
// FoundExperience = UserExperience;
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// if (UserExperience->bIsDefaultExperience && DefaultExperience == nullptr)
|
||||
// {
|
||||
// DefaultExperience = UserExperience;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (FoundExperience == nullptr)
|
||||
// {
|
||||
// FoundExperience = DefaultExperience;
|
||||
// }
|
||||
//
|
||||
// UGameInstance* GameInstance = GetGameInstance();
|
||||
// if (ensure(FoundExperience && GameInstance))
|
||||
// {
|
||||
// // Actually host the game
|
||||
// UCommonSession_HostSessionRequest* HostRequest = FoundExperience->CreateHostingRequest(this);
|
||||
// if (ensure(HostRequest))
|
||||
// {
|
||||
// HostRequest->OnlineMode = OnlineMode;
|
||||
//
|
||||
// // TODO override other parameters?
|
||||
//
|
||||
// UCommonSessionSubsystem* SessionSubsystem = GameInstance->GetSubsystem<UCommonSessionSubsystem>();
|
||||
// SessionSubsystem->HostSession(nullptr, HostRequest);
|
||||
//
|
||||
// // This will handle the map travel
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void AOLSGameMode::OnUserInitializedForDedicatedServer(
|
||||
const UCommonUserInfo* userInfo,
|
||||
bool isSuccess,
|
||||
FText error,
|
||||
ECommonUserPrivilege requestedPrivilege,
|
||||
ECommonUserOnlineContext onlineContext)
|
||||
{
|
||||
UGameInstance* gameInstance = GetGameInstance();
|
||||
if (gameInstance)
|
||||
{
|
||||
// Unbind
|
||||
UCommonUserSubsystem* UserSubsystem = gameInstance->GetSubsystem<UCommonUserSubsystem>();
|
||||
UserSubsystem->OnUserInitializeComplete.RemoveDynamic(this, &ThisClass::OnUserInitializedForDedicatedServer);
|
||||
|
||||
// Dedicated servers do not require user login, but some online subsystems may expect it
|
||||
if (isSuccess && ensure(userInfo))
|
||||
{
|
||||
// @TODO: replace this with our custom.
|
||||
// UE_LOG(LogLyraExperience, Log, TEXT("Dedicated server user login succeeded for id %s, starting online server"), *UserInfo->GetNetId().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO: replace this with our custom.
|
||||
// UE_LOG(LogLyraExperience, Log, TEXT("Dedicated server user login unsuccessful, starting online server as login is not required"));
|
||||
}
|
||||
|
||||
HostDedicatedServerMatch(ECommonSessionOnlineMode::Online);
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@
|
||||
#include "AbilitySystem/OLSAbilitySystemComponent.h"
|
||||
#include "Components/GameFrameworkComponentManager.h"
|
||||
#include "DataAssets/OLSAbilitySetPrimaryDataAsset.h"
|
||||
#include "GameModes/OLSExperienceManagerComponent.h"
|
||||
#include "GameModes/OLSGameMode.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "Player/OLSPlayerController.h"
|
||||
|
||||
@ -18,12 +20,6 @@ AOLSPlayerState::AOLSPlayerState(const FObjectInitializer& objectInitializer) :
|
||||
// Create attribute sets here.
|
||||
}
|
||||
|
||||
template <class T>
|
||||
const T* AOLSPlayerState::GetPawnData() const
|
||||
{
|
||||
return Cast<T>(PawnData);
|
||||
}
|
||||
|
||||
void AOLSPlayerState::SetPawnData(const UOLSPawnDataAsset* pawnData)
|
||||
{
|
||||
check(pawnData);
|
||||
@ -55,6 +51,42 @@ void AOLSPlayerState::SetPawnData(const UOLSPawnDataAsset* pawnData)
|
||||
ForceNetUpdate();
|
||||
}
|
||||
|
||||
void AOLSPlayerState::AddStatTagStack(FGameplayTag tag, int32 stackCount)
|
||||
{
|
||||
StatTags.AddStack(tag, stackCount);
|
||||
}
|
||||
|
||||
void AOLSPlayerState::RemoveStatTagStack(FGameplayTag tag, int32 stackCount)
|
||||
{
|
||||
StatTags.RemoveStack(tag, stackCount);
|
||||
}
|
||||
|
||||
int32 AOLSPlayerState::GetStatTagStackCount(FGameplayTag tag) const
|
||||
{
|
||||
return StatTags.GetStackCount(tag);
|
||||
}
|
||||
|
||||
bool AOLSPlayerState::HasStatTag(FGameplayTag tag) const
|
||||
{
|
||||
return StatTags.ContainsTag(tag);
|
||||
}
|
||||
|
||||
void AOLSPlayerState::OnExperienceLoaded(const UOLSExperienceDefinitionDataAsset* currentExperience)
|
||||
{
|
||||
if (AOLSGameMode* gameMode = GetWorld()->GetAuthGameMode<AOLSGameMode>())
|
||||
{
|
||||
if (const UOLSPawnDataAsset* newPawnData = gameMode->GetPawnDataForController(GetOwningController()))
|
||||
{
|
||||
SetPawnData(newPawnData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// @T ODO: Replace this with our custom.
|
||||
// UE_LOG(LogLyra, Error, TEXT("ALyraPlayerState::OnExperienceLoaded(): Unable to find PawnData to initialize player state [%s]!"), *GetNameSafe(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AOLSPlayerState::OnRep_PawnData()
|
||||
{
|
||||
}
|
||||
@ -71,9 +103,9 @@ void AOLSPlayerState::PostInitializeComponents()
|
||||
{
|
||||
const TObjectPtr<AGameStateBase> gameState = GetWorld()->GetGameState();
|
||||
check(gameState);
|
||||
// ULyraExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<ULyraExperienceManagerComponent>();
|
||||
// check(ExperienceComponent);
|
||||
// ExperienceComponent->CallOrRegister_OnExperienceLoaded(FOnLyraExperienceLoaded::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded));
|
||||
UOLSExperienceManagerComponent* ExperienceComponent = gameState->FindComponentByClass<UOLSExperienceManagerComponent>();
|
||||
check(ExperienceComponent);
|
||||
ExperienceComponent->CallOrRegister_OnExperienceLoaded(FOLSExperienceLoadedNativeDelegate::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "Misc/ScopedSlowTask.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Systems/OLSAssetManagerStartupJob.h"
|
||||
#include "DataAssets/OLSPawnPrimaryDataAsset.h"
|
||||
#include "DataAssets/OLSPawnDataAsset.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSAssetManager)
|
||||
|
||||
@ -105,7 +105,7 @@ void UOLSAssetManager::DumpLoadedAssets()
|
||||
{
|
||||
}
|
||||
|
||||
const UOLSPawnPrimaryDataAsset* UOLSAssetManager::GetDefaultPawnData() const
|
||||
const UOLSPawnDataAsset* UOLSAssetManager::GetDefaultPawnData() const
|
||||
{
|
||||
return GetAsset(DefaultPawnData);
|
||||
}
|
||||
|
104
Source/ols/Private/Systems/OLSGameplayTagStack.cpp
Normal file
104
Source/ols/Private/Systems/OLSGameplayTagStack.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
||||
|
||||
|
||||
#include "Systems/OLSGameplayTagStack.h"
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// FGameplayTagStack
|
||||
|
||||
FString FOLSGameplayTagStack::GetDebugString() const
|
||||
{
|
||||
return FString::Printf(TEXT("%sx%d"), *Tag.ToString(), StackCount);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// FGameplayTagStackContainer
|
||||
|
||||
void FOLSGameplayTagStackContainer::AddStack(FGameplayTag tag, int32 stackCount)
|
||||
{
|
||||
if (!tag.IsValid())
|
||||
{
|
||||
FFrame::KismetExecutionMessage(TEXT("An invalid tag was passed to AddStack"), ELogVerbosity::Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stackCount > 0)
|
||||
{
|
||||
for (FOLSGameplayTagStack& stack : Stacks)
|
||||
{
|
||||
if (stack.Tag == tag)
|
||||
{
|
||||
const int32 newCount = stack.StackCount + stackCount;
|
||||
stack.StackCount = newCount;
|
||||
TagToCountMap[tag] = newCount;
|
||||
MarkItemDirty(stack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FOLSGameplayTagStack& newStack = Stacks.Emplace_GetRef(tag, stackCount);
|
||||
MarkItemDirty(newStack);
|
||||
TagToCountMap.Add(tag, stackCount);
|
||||
}
|
||||
}
|
||||
|
||||
void FOLSGameplayTagStackContainer::RemoveStack(FGameplayTag tag, int32 stackCount)
|
||||
{
|
||||
if (!tag.IsValid())
|
||||
{
|
||||
FFrame::KismetExecutionMessage(TEXT("An invalid tag was passed to RemoveStack"), ELogVerbosity::Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
//@TODO: Should we error if you try to remove a stack that doesn't exist or has a smaller count?
|
||||
if (stackCount > 0)
|
||||
{
|
||||
for (auto it = Stacks.CreateIterator(); it; ++it)
|
||||
{
|
||||
FOLSGameplayTagStack& stack = *it;
|
||||
if (stack.Tag == tag)
|
||||
{
|
||||
if (stack.StackCount <= stackCount)
|
||||
{
|
||||
it.RemoveCurrent();
|
||||
TagToCountMap.Remove(tag);
|
||||
MarkArrayDirty();
|
||||
}
|
||||
else
|
||||
{
|
||||
const int32 newCount = stack.StackCount - stackCount;
|
||||
stack.StackCount = newCount;
|
||||
TagToCountMap[tag] = newCount;
|
||||
MarkItemDirty(stack);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FOLSGameplayTagStackContainer::PreReplicatedRemove(const TArrayView<int32> removedIndices, int32 finalSize)
|
||||
{
|
||||
for (int32 index : removedIndices)
|
||||
{
|
||||
const FGameplayTag tag = Stacks[index].Tag;
|
||||
TagToCountMap.Remove(tag);
|
||||
}
|
||||
}
|
||||
|
||||
void FOLSGameplayTagStackContainer::PostReplicatedAdd(const TArrayView<int32> addedIndices, int32 finalSize)
|
||||
{
|
||||
for (int32 index : addedIndices)
|
||||
{
|
||||
const FOLSGameplayTagStack& stack = Stacks[index];
|
||||
TagToCountMap.Add(stack.Tag, stack.StackCount);
|
||||
}
|
||||
}
|
||||
|
||||
void FOLSGameplayTagStackContainer::PostReplicatedChange(const TArrayView<int32> changedIndices, int32 finalSize)
|
||||
{
|
||||
for (int32 index : changedIndices)
|
||||
{
|
||||
const FOLSGameplayTagStack& stack = Stacks[index];
|
||||
TagToCountMap[stack.Tag] = stack.StackCount;
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
|
||||
namespace UE::GameFeatures { struct FResult; }
|
||||
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FOnOLSExperienceLoaded, const class UOLSExperienceDefinitionDataAsset* /*experience*/);
|
||||
DECLARE_MULTICAST_DELEGATE_OneParam(FOLSExperienceLoadedNativeDelegate, const class UOLSExperienceDefinitionDataAsset* /*experience*/);
|
||||
|
||||
enum class EOLSExperienceLoadState
|
||||
{
|
||||
@ -55,15 +55,15 @@ public:
|
||||
// Ensures the delegate is called once the experience has been loaded,
|
||||
// before others are called.
|
||||
// However, if the experience has already loaded, calls the delegate immediately.
|
||||
void CallOrRegister_OnExperienceLoaded_HighPriority(FOnOLSExperienceLoaded::FDelegate&& delegate);
|
||||
void CallOrRegister_OnExperienceLoaded_HighPriority(FOLSExperienceLoadedNativeDelegate::FDelegate&& delegate);
|
||||
|
||||
// Ensures the delegate is called once the experience has been loaded
|
||||
// If the experience has already loaded, calls the delegate immediately
|
||||
void CallOrRegister_OnExperienceLoaded(FOnOLSExperienceLoaded::FDelegate&& delegate);
|
||||
void CallOrRegister_OnExperienceLoaded(FOLSExperienceLoadedNativeDelegate::FDelegate&& delegate);
|
||||
|
||||
// Ensures the delegate is called once the experience has been loaded
|
||||
// If the experience has already loaded, calls the delegate immediately
|
||||
void CallOrRegister_OnExperienceLoaded_LowPriority(FOnOLSExperienceLoaded::FDelegate&& delegate);
|
||||
void CallOrRegister_OnExperienceLoaded_LowPriority(FOLSExperienceLoadedNativeDelegate::FDelegate&& delegate);
|
||||
|
||||
private:
|
||||
|
||||
@ -99,11 +99,11 @@ private:
|
||||
* Delegate called when the experience has finished loading just before others
|
||||
* (e.g., subsystems that set up for regular gameplay)
|
||||
*/
|
||||
FOnOLSExperienceLoaded OnExperienceLoaded_HighPriority;
|
||||
FOLSExperienceLoadedNativeDelegate OnExperienceLoaded_HighPriority;
|
||||
|
||||
/** Delegate called when the experience has finished loading */
|
||||
FOnOLSExperienceLoaded OnExperienceLoaded;
|
||||
FOLSExperienceLoadedNativeDelegate OnExperienceLoaded;
|
||||
|
||||
/** Delegate called when the experience has finished loading */
|
||||
FOnOLSExperienceLoaded OnExperienceLoaded_LowPriority;
|
||||
FOLSExperienceLoadedNativeDelegate OnExperienceLoaded_LowPriority;
|
||||
};
|
||||
|
77
Source/ols/Public/GameModes/OLSGameMode.h
Normal file
77
Source/ols/Public/GameModes/OLSGameMode.h
Normal file
@ -0,0 +1,77 @@
|
||||
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "CommonSessionSubsystem.h"
|
||||
#include "ModularGameplayActors/OLSModularGameMode.h"
|
||||
#include "OLSGameMode.generated.h"
|
||||
|
||||
/**
|
||||
* Post login event, triggered when a player or bot joins the game as well as after seamless and non seamless travel
|
||||
*
|
||||
* This is called after the player has finished initialization
|
||||
*/
|
||||
DECLARE_MULTICAST_DELEGATE_TwoParams(FOLSGameModePlayerInitializeNativeDelegate, class AGameModeBase* /*GameMode*/,
|
||||
class AController* /*NewPlayer*/);
|
||||
|
||||
|
||||
/**
|
||||
* AOLSGameMode
|
||||
*
|
||||
* The base game mode class used by this project.
|
||||
*/
|
||||
UCLASS(Config = Game, Meta = (ShortTooltip = "The base game mode class used by this project."))
|
||||
class OLS_API AOLSGameMode : public AOLSModularGameModeBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
// AOLSGameMode(const FObjectInitializer& objectInitializer);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "OLS|Pawn")
|
||||
const class UOLSPawnDataAsset* GetPawnDataForController(const class AController* controller) const;
|
||||
|
||||
//~ Begin AGameModeBase interface
|
||||
virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;
|
||||
virtual UClass* GetDefaultPawnClassForController_Implementation(AController* controller) override;
|
||||
virtual APawn* SpawnDefaultPawnAtTransform_Implementation(AController* newPlayer, const FTransform& spawnTransform) override;
|
||||
virtual bool ShouldSpawnAtStartSpot(AController* Player) override;
|
||||
virtual void HandleStartingNewPlayer_Implementation(APlayerController* newPlayer) override;
|
||||
virtual AActor* ChoosePlayerStart_Implementation(AController* player) override;
|
||||
virtual void FinishRestartPlayer(AController* newPlayer, const FRotator& startRotation) override;
|
||||
virtual bool PlayerCanRestart_Implementation(APlayerController* player) override;
|
||||
virtual void InitGameState() override;
|
||||
virtual bool UpdatePlayerStartSpot(AController* player, const FString& portal, FString& outErrorMessage) override;
|
||||
virtual void GenericPlayerInitialization(AController* newPlayer) override;
|
||||
virtual void FailedToRestartPlayer(AController* newPlayer) override;
|
||||
//~ End AGameModeBase interface
|
||||
|
||||
// Restart (respawn) the specified player or bot next frame
|
||||
// - If bForceReset is true, the controller will be reset this frame (abandoning the currently possessed pawn, if any)
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void RequestPlayerRestartNextFrame(AController* controller, bool shouldForceReset = false);
|
||||
|
||||
// Agnostic version of PlayerCanRestart that can be used for both player bots and players
|
||||
virtual bool ControllerCanRestart(AController* controller);
|
||||
|
||||
// Delegate called on player initialization, described above
|
||||
FOLSGameModePlayerInitializeNativeDelegate OnGameModePlayerInitialized;
|
||||
|
||||
protected:
|
||||
|
||||
void OnExperienceLoaded(const class UOLSExperienceDefinitionDataAsset* currentExperience);
|
||||
bool IsExperienceLoaded() const;
|
||||
|
||||
void OnMatchAssignmentGiven(FPrimaryAssetId experienceId, const FString& experienceIdSource);
|
||||
|
||||
void HandleMatchAssignmentIfNotExpectingOne();
|
||||
|
||||
bool TryDedicatedServerLogin();
|
||||
void HostDedicatedServerMatch(ECommonSessionOnlineMode onlineMode);
|
||||
|
||||
UFUNCTION()
|
||||
void OnUserInitializedForDedicatedServer(const UCommonUserInfo* userInfo, bool isSuccess, FText error, ECommonUserPrivilege requestedPrivilege, ECommonUserOnlineContext onlineContext);
|
||||
|
||||
};
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ModularGameplayActors/OLSModularPlayerState.h"
|
||||
#include "Systems/OLSGameplayTagStack.h"
|
||||
#include "OLSPlayerState.generated.h"
|
||||
|
||||
|
||||
@ -37,10 +38,39 @@ public:
|
||||
class UOLSAbilitySystemComponent* GetOLSAbilitySystemComponent() const;
|
||||
|
||||
template <class T>
|
||||
const T* GetPawnData() const;
|
||||
const T* GetPawnData() const
|
||||
{
|
||||
return Cast<T>(PawnData);
|
||||
}
|
||||
|
||||
void SetPawnData(const class UOLSPawnDataAsset* pawnData);
|
||||
|
||||
public:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Stats
|
||||
|
||||
// Adds a specified number of stacks to the tag (does nothing if StackCount is below 1)
|
||||
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category= "OLS|Stats")
|
||||
void AddStatTagStack(FGameplayTag tag, int32 stackCount);
|
||||
|
||||
// Removes a specified number of stacks from the tag (does nothing if StackCount is below 1)
|
||||
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category= "OLS|Stats")
|
||||
void RemoveStatTagStack(FGameplayTag tag, int32 stackCount);
|
||||
|
||||
// Returns the stack count of the specified tag (or 0 if the tag is not present)
|
||||
UFUNCTION(BlueprintCallable, Category= "OLS|Stats")
|
||||
int32 GetStatTagStackCount(FGameplayTag tag) const;
|
||||
|
||||
// Returns true if there is at least one stack of the specified tag
|
||||
UFUNCTION(BlueprintCallable, Category= "OLS|Stats")
|
||||
bool HasStatTag(FGameplayTag tag) const;
|
||||
|
||||
private:
|
||||
|
||||
void OnExperienceLoaded(const class UOLSExperienceDefinitionDataAsset* currentExperience);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
UFUNCTION()
|
||||
@ -50,4 +80,9 @@ protected:
|
||||
|
||||
UPROPERTY(ReplicatedUsing = OnRep_PawnData)
|
||||
TObjectPtr<const class UOLSPawnDataAsset> PawnData = nullptr;
|
||||
|
||||
private:
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
FOLSGameplayTagStackContainer StatTags;
|
||||
};
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
|
||||
// @Todo implement this function.
|
||||
// const class UOLSPawnPrimaryDataAsset& GetGameData();
|
||||
const class UOLSPawnPrimaryDataAsset* GetDefaultPawnData() const;
|
||||
const class UOLSPawnDataAsset* GetDefaultPawnData() const;
|
||||
|
||||
protected:
|
||||
|
||||
@ -93,7 +93,7 @@ protected:
|
||||
|
||||
// Pawn data used when spawning player pawns if there isn't one set on the player state.
|
||||
UPROPERTY(Config)
|
||||
TSoftObjectPtr<class UOLSPawnPrimaryDataAsset> DefaultPawnData;
|
||||
TSoftObjectPtr<class UOLSPawnDataAsset> DefaultPawnData;
|
||||
|
||||
private:
|
||||
|
||||
|
97
Source/ols/Public/Systems/OLSGameplayTagStack.h
Normal file
97
Source/ols/Public/Systems/OLSGameplayTagStack.h
Normal file
@ -0,0 +1,97 @@
|
||||
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Net/Serialization/FastArraySerializer.h"
|
||||
#include "OLSGameplayTagStack.generated.h"
|
||||
|
||||
/**
|
||||
* Represents one stack of a gameplay tag (tag + count)
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct OLS_API FOLSGameplayTagStack : public FFastArraySerializerItem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FOLSGameplayTagStack()
|
||||
{}
|
||||
|
||||
FOLSGameplayTagStack(FGameplayTag InTag, int32 InStackCount)
|
||||
: Tag(InTag)
|
||||
, StackCount(InStackCount)
|
||||
{
|
||||
}
|
||||
|
||||
FString GetDebugString() const;
|
||||
|
||||
private:
|
||||
|
||||
friend struct FOLSGameplayTagStackContainer;
|
||||
|
||||
UPROPERTY()
|
||||
FGameplayTag Tag = FGameplayTag::EmptyTag;
|
||||
|
||||
UPROPERTY()
|
||||
int32 StackCount = 0;
|
||||
};
|
||||
|
||||
|
||||
/** Container of gameplay tag stacks */
|
||||
USTRUCT(BlueprintType)
|
||||
struct OLS_API FOLSGameplayTagStackContainer : public FFastArraySerializer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FOLSGameplayTagStackContainer()
|
||||
// : Owner(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
// Adds a specified number of stacks to the tag (does nothing if StackCount is below 1)
|
||||
void AddStack(FGameplayTag tag, int32 stackCount);
|
||||
|
||||
// Removes a specified number of stacks from the tag (does nothing if StackCount is below 1)
|
||||
void RemoveStack(FGameplayTag tag, int32 stackCount);
|
||||
|
||||
// Returns the stack count of the specified tag (or 0 if the tag is not present)
|
||||
int32 GetStackCount(FGameplayTag Tag) const
|
||||
{
|
||||
return TagToCountMap.FindRef(Tag);
|
||||
}
|
||||
|
||||
// Returns true if there is at least one stack of the specified tag
|
||||
bool ContainsTag(FGameplayTag Tag) const
|
||||
{
|
||||
return TagToCountMap.Contains(Tag);
|
||||
}
|
||||
|
||||
//~ Begin FFastArraySerializer contract
|
||||
void PreReplicatedRemove(const TArrayView<int32> removedIndices, int32 finalSize);
|
||||
void PostReplicatedAdd(const TArrayView<int32> addedIndices, int32 finalSize);
|
||||
void PostReplicatedChange(const TArrayView<int32> changedIndices, int32 finalSize);
|
||||
//~ End FFastArraySerializer contract
|
||||
|
||||
bool NetDeltaSerialize(FNetDeltaSerializeInfo& DeltaParms)
|
||||
{
|
||||
return FFastArraySerializer::FastArrayDeltaSerialize<FOLSGameplayTagStack, FOLSGameplayTagStackContainer>(Stacks, DeltaParms, *this);
|
||||
}
|
||||
|
||||
private:
|
||||
// Replicated list of gameplay tag stacks
|
||||
UPROPERTY()
|
||||
TArray<FOLSGameplayTagStack> Stacks;
|
||||
|
||||
// Accelerated list of tag stacks for queries
|
||||
TMap<FGameplayTag, int32> TagToCountMap;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct TStructOpsTypeTraits<FOLSGameplayTagStackContainer> : public TStructOpsTypeTraitsBase2<FOLSGameplayTagStackContainer>
|
||||
{
|
||||
enum
|
||||
{
|
||||
WithNetDeltaSerializer = true,
|
||||
};
|
||||
};
|
@ -19,7 +19,7 @@ public class ols : ModuleRules
|
||||
"GameFeatures",
|
||||
"ModularGameplay",
|
||||
"EnhancedInput",
|
||||
"OLSAnimation", "AIModule", "CommonLoadingScreen",
|
||||
"OLSAnimation", "AIModule", "CommonLoadingScreen", "CommonUser"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new[]
|
||||
|
Loading…
Reference in New Issue
Block a user