#include "OnlineSampleOnlineSubsystem.h" #include "Online/CoreOnline.h" #include "Online/OnlineResult.h" #include "Online/OnlineAsyncOpHandle.h" #include "Online/OnlineError.h" #include "Online/OnlineServices.h" #include "Online/Auth.h" #include "Online/TitleFile.h" DEFINE_LOG_CATEGORY(LogOnlineSampleOnlineSubsystem); /// <summary> /// Whether to create this subsystem. For simplicity, the subsystem is only /// created on clients and standalone games, not servers. This function /// is often used to limit creation of subsystems to a server or client. /// Be sure to null-check subsystem before usage! /// </summary> /// <param name="Outer"></param> /// <returns>Boolean whether or not to create this subsystem</returns> bool UOnlineSampleOnlineSubsystem::ShouldCreateSubsystem(UObject* Outer) const { #if UE_SERVER return false; #else return Super::ShouldCreateSubsystem(Outer); #endif } /// <summary> /// Initialize called after the Game Instance is initialized /// </summary> /// <param name="Collection">Collection of subsystems that are initialized by the game instance</param> void UOnlineSampleOnlineSubsystem::Initialize(FSubsystemCollectionBase& Collection) { UE_LOG(LogTemp, Log, TEXT("OnlineSampleOnlineSubsystem initialized.")); Super::Initialize(Collection); // Initialize online services InitializeOnlineServices(); } /// <summary> /// Deinitialize called before the Game Instance is deinitialized/shutdown /// </summary> void UOnlineSampleOnlineSubsystem::Deinitialize() { UE_LOG(LogTemp, Log, TEXT("OnlineSampleOnlineSubsystem deinitialized.")); // Unbind event handles and reset struct info OnlineServicesInfoInternal->Reset(); // Deinitialize parent class Super::Deinitialize(); } /// <summary> /// Handle the asynchronous EnumerateFiles function. Log if there is a failure. /// Success calls the asynchronous ReadFile function handled by HandleReadFile. /// </summary> /// <param name="EnumerateFilesResult">Result of Enumerate Files attempt</param> /// <param name="OnlineUser">User that queried for title file</param> /// <param name="Filename">Name of file user queried</param> void UOnlineSampleOnlineSubsystem::HandleEnumerateFiles(const UE::Online::TOnlineResult<UE::Online::FTitleFileEnumerateFiles>& EnumerateFilesResult, TObjectPtr<UOnlineUserInfo> OnlineUser, FString Filename) { using namespace UE::Online; if (EnumerateFilesResult.IsOk()) { FTitleFileGetEnumeratedFiles::Params GetParams; GetParams.LocalAccountId = OnlineUser->AccountId; TOnlineResult<FTitleFileGetEnumeratedFiles> GetResult = OnlineServicesInfoInternal->TitleFileInterface->GetEnumeratedFiles(MoveTemp(GetParams)); if (GetResult.IsOk()) { FTitleFileGetEnumeratedFiles::Result& CachedFiles = GetResult.GetOkValue(); int32 FileIndex = CachedFiles.Filenames.Find(Filename); if (FileIndex == INDEX_NONE) { UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Title File \"%s\" not found!"), *Filename); } else { FTitleFileReadFile::Params ReadParams; ReadParams.LocalAccountId = OnlineUser->AccountId; ReadParams.Filename = Filename; OnlineServicesInfoInternal->TitleFileInterface->ReadFile(MoveTemp(ReadParams)).OnComplete(this, &ThisClass::HandleReadFile, Filename); } } else { UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Get Title File Error: %s"), *GetResult.GetErrorValue().GetLogString()); } } else { UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Enum Title File Error: %s"), *EnumerateFilesResult.GetErrorValue().GetLogString()); } } /// <summary> /// Handle the asynchronous ReadFile function. Log if there is a failure. /// Success populates the user's TitleFileContent. /// </summary> /// <param name="ReadFileResult">Result of ReadFile attempt</param> /// <param name="Filename">Name of file to read</param> void UOnlineSampleOnlineSubsystem::HandleReadFile(const UE::Online::TOnlineResult<UE::Online::FTitleFileReadFile>& ReadFileResult, FString Filename) { using namespace UE::Online; if (ReadFileResult.IsOk()) { const FTitleFileReadFile::Result& ReadFileResultValue = ReadFileResult.GetOkValue(); OnlineServicesInfoInternal->TitleFileContent = *ReadFileResultValue.FileContents; } else { UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Read Title File Error: %s"), *ReadFileResult.GetErrorValue().GetLogString()); } } /// <summary> /// Initialize the Online Services: /// Obtain pointer to services /// Obtain pointers to interfaces /// Add event handles /// Check pointer validity /// </summary> void UOnlineSampleOnlineSubsystem::InitializeOnlineServices() { OnlineServicesInfoInternal = new FOnlineServicesInfo(); // Initialize Services ptr OnlineServicesInfoInternal->OnlineServices = UE::Online::GetServices(); check(OnlineServicesInfoInternal->OnlineServices.IsValid()); // Verify Services type OnlineServicesInfoInternal->OnlineServicesType = OnlineServicesInfoInternal->OnlineServices->GetServicesProvider(); if (OnlineServicesInfoInternal->OnlineServices.IsValid()) { // Initialize Interface ptrs OnlineServicesInfoInternal->AuthInterface = OnlineServicesInfoInternal->OnlineServices->GetAuthInterface(); check(OnlineServicesInfoInternal->AuthInterface.IsValid()); OnlineServicesInfoInternal->TitleFileInterface = OnlineServicesInfoInternal->OnlineServices->GetTitleFileInterface(); check(OnlineServicesInfoInternal->TitleFileInterface.IsValid()); } else { UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Error: Failed to initialize services.")); } } /// <summary> /// EnumerateFiles, GetEnumeratedFiles, and ReadFile. This implementation uses a lambda function /// to handle the OnComplete callback. /// </summary> /// <param name="Filename">File to read</param> /// <param name="PlatformUserId">User to retrieve file for</param> void UOnlineSampleOnlineSubsystem::RetrieveTitleFile(FString Filename, FPlatformUserId PlatformUserId) { using namespace UE::Online; FTitleFileEnumerateFiles::Params EnumParams; FAccountId LocalAccountId; TObjectPtr<UOnlineUserInfo> OnlineUser; if (OnlineUserInfos.Contains(PlatformUserId)) { OnlineUser = *OnlineUserInfos.Find(PlatformUserId); LocalAccountId = OnlineUser->AccountId; EnumParams.LocalAccountId = LocalAccountId; if (OnlineServicesInfoInternal->TitleFileInterface.IsValid()) { (OnlineServicesInfoInternal->TitleFileInterface)->EnumerateFiles(MoveTemp(EnumParams)).OnComplete(this, &ThisClass::HandleEnumerateFiles, OnlineUser, Filename); } else { UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Title File Interface pointer invalid.")); } } else { UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Could not find user with Platform User Id: %d"), PlatformUserId.GetInternalId()); } } /// <summary> /// Register online user with local registry OnlineUserInfos /// </summary> /// <param name="PlatformUserId">Platform user id of user to register</param> void UOnlineSampleOnlineSubsystem::RegisterLocalOnlineUser(FPlatformUserId PlatformUserId) { using namespace UE::Online; FAuthGetLocalOnlineUserByPlatformUserId::Params GetUserParams; GetUserParams.PlatformUserId = PlatformUserId; if (OnlineServicesInfoInternal->AuthInterface.IsValid()) { TOnlineResult<FAuthGetLocalOnlineUserByPlatformUserId> AuthGetResult = OnlineServicesInfoInternal->AuthInterface->GetLocalOnlineUserByPlatformUserId(MoveTemp(GetUserParams)); if (AuthGetResult.IsOk()) { FAuthGetLocalOnlineUserByPlatformUserId::Result& LocalOnlineUser = AuthGetResult.GetOkValue(); TSharedRef<FAccountInfo> UserAccountInfo = LocalOnlineUser.AccountInfo; FAccountInfo UserAccountInfoContent = *UserAccountInfo; if (!OnlineUserInfos.Contains(UserAccountInfoContent.PlatformUserId)) { UOnlineUserInfo* NewUser = CreateAndRegisterUserInfo(UserAccountInfoContent.AccountId.GetHandle(), PlatformUserId, UserAccountInfoContent.AccountId, UserAccountInfoContent.AccountId.GetOnlineServicesType()); UE_LOG(LogOnlineSampleOnlineSubsystem, Log, TEXT("Local User Registered: %s"), *(NewUser->DebugInfoToString())); } else { UE_LOG(LogOnlineSampleOnlineSubsystem, Log, TEXT("Local User with platform user id %d already registered."), PlatformUserId.GetInternalId()); } } else { FOnlineError ErrorResult = AuthGetResult.GetErrorValue(); UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Get Local Online User Error: %s"), *ErrorResult.GetLogString()); } } else { UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Auth Interface pointer invalid.")); } } /// <summary> /// Obtain title file and read its contents. /// </summary> /// <param name="Filename">File to read</param> /// <param name="PlatformUserId">User to read file for</param> /// <returns>FileString with contents of Filename</returns> FString UOnlineSampleOnlineSubsystem::ReadTitleFile(FString Filename, FPlatformUserId PlatformUserId) { using namespace UE::Online; RetrieveTitleFile(Filename, PlatformUserId); FTitleFileContents FileContents = OnlineServicesInfoInternal->TitleFileContent; FString FileString = FString(FileContents.Num(), UTF8_TO_TCHAR(FileContents.GetData())); UE_LOG(LogOnlineSampleOnlineSubsystem, Log, TEXT("Reading Title File: %s"), *Filename); return FileString; } /// <summary> /// Create a UOnlineUserInfo object from the given information. /// </summary> /// <param name="LocalUserIndex"></param> /// <param name="PlatformUserId"></param> /// <param name="AccountId"></param> /// <param name="Services">Online services user is registered with</param> /// <returns>Object pointer to the NewUser</returns> TObjectPtr<UOnlineUserInfo> UOnlineSampleOnlineSubsystem::CreateOnlineUserInfo(int32 LocalUserIndex, FPlatformUserId PlatformUserId, UE::Online::FAccountId AccountId, UE::Online::EOnlineServices Services) { TObjectPtr<UOnlineUserInfo> NewUser = NewObject<UOnlineUserInfo>(this); NewUser->LocalUserIndex = LocalUserIndex; NewUser->PlatformUserId = PlatformUserId; NewUser->AccountId = AccountId; NewUser->Services = Services; return NewUser; } /// <summary> /// Create a UOnlineUserInfo object by calling CreateOnlineUserInfo then /// register the user with the local registry OnlineUserInfos /// </summary> /// <param name="LocalUserIndex"></param> /// <param name="PlatformUserId"></param> /// <param name="AccountId"></param> /// <param name="Services">Online services user is registered with</param> /// <returns>Object pointer to the NewUser</returns> TObjectPtr<UOnlineUserInfo> UOnlineSampleOnlineSubsystem::CreateAndRegisterUserInfo(int32 LocalUserIndex, FPlatformUserId PlatformUserId, UE::Online::FAccountId AccountId, UE::Online::EOnlineServices Services) { TObjectPtr<UOnlineUserInfo> NewUser = CreateOnlineUserInfo(LocalUserIndex, PlatformUserId, AccountId, Services); OnlineUserInfos.Add(PlatformUserId, NewUser); return NewUser; } /// <summary> /// Get UOnlineUserInfo for provided platform user id. /// </summary> /// <param name="PlatformUserId">id of user to retrieve</param> /// <returns>Object pointer to OnlineUser</returns> TObjectPtr<UOnlineUserInfo> UOnlineSampleOnlineSubsystem::GetOnlineUserInfo(FPlatformUserId PlatformUserId) { TObjectPtr<UOnlineUserInfo> OnlineUser; if (OnlineUserInfos.Contains(PlatformUserId)) { OnlineUser = *OnlineUserInfos.Find(PlatformUserId); } else { UE_LOG(LogOnlineSampleOnlineSubsystem, Error, TEXT("Could not find user with Platform User Id: %d"), PlatformUserId.GetInternalId()); OnlineUser = nullptr; } return OnlineUser; } /// <summary> /// Constructor for UOnlineUserInfo object /// </summary> UOnlineUserInfo::UOnlineUserInfo() { } /// <summary> /// Return debug string for UOnlineUserInfo /// </summary> /// <returns>String representation of UOnlineUserInfo</returns> const FString UOnlineUserInfo::DebugInfoToString() { int32 UserIndex = this->LocalUserIndex; int32 PlatformId = this->PlatformUserId; TArray<FStringFormatArg> FormatArgs; FormatArgs.Add(FStringFormatArg(UserIndex)); FormatArgs.Add(FStringFormatArg(PlatformId)); return FString::Format(TEXT("LocalUserNumber: {0}, PlatformUserId: {1}"), FormatArgs); }