// Copyright Epic Games, Inc. All Rights Reserved. #include "OnlineIdentityInterfaceSteam.h" #include "Misc/ConfigCacheIni.h" #include "OnlineAuthInterfaceSteam.h" #include "OnlineEncryptedAppTicketInterfaceSteam.h" #include "OnlineSubsystemSteam.h" #include "OnlineSubsystemSteamTypes.h" #include "OnlineError.h" #include FOnlineIdentitySteam::FOnlineIdentitySteam(FOnlineSubsystemSteam* InSubsystem) : SteamUserPtr(NULL), SteamFriendsPtr(NULL), SteamSubsystem(InSubsystem) { SteamUserPtr = SteamUser(); SteamFriendsPtr = SteamFriends(); } TSharedPtr FOnlineIdentitySteam::GetUserAccount(const FUniqueNetId& UserId) const { //@todo - not implemented return NULL; } TArray > FOnlineIdentitySteam::GetAllUserAccounts() const { //@todo - not implemented return TArray >(); } bool FOnlineIdentitySteam::Login(int32 LocalUserNum, const FOnlineAccountCredentials& AccountCredentials) { FString ErrorStr; if (LocalUserNum < MAX_LOCAL_PLAYERS) { // Double check they are properly logged in if (SteamUserPtr != NULL && // Login is handled by steam SteamUserPtr->BLoggedOn()) { // Login changed delegate TriggerOnLoginChangedDelegates(LocalUserNum); // Login completion delegate TriggerOnLoginCompleteDelegates(LocalUserNum, true, *FUniqueNetIdSteam::Create(SteamUserPtr->GetSteamID()), TEXT("")); return true; } else { // User is not currently logged into Steam ErrorStr = TEXT("Not logged in or no connection."); } } else { // Requesting a local user is always invalid ErrorStr = FString::Printf(TEXT("Invalid user %d"),LocalUserNum); } if (!ErrorStr.IsEmpty()) { UE_LOG_ONLINE_IDENTITY(Warning, TEXT("Failed Steam login. %s"), *ErrorStr); TriggerOnLoginCompleteDelegates(LocalUserNum, false, *FUniqueNetIdSteam::EmptyId(), ErrorStr); } return false; } bool FOnlineIdentitySteam::Logout(int32 LocalUserNum) { TriggerOnLogoutCompleteDelegates(LocalUserNum,false); return false; } bool FOnlineIdentitySteam::AutoLogin(int32 LocalUserNum) { if (!IsRunningDedicatedServer()) { // Double check they are properly logged in if (SteamUserPtr != NULL && // Login is handled by steam SteamUserPtr->BLoggedOn()) { // Login changed delegate TriggerOnLoginChangedDelegates(LocalUserNum); // Login completion delegate FString AuthToken = GetAuthToken(LocalUserNum); TriggerOnLoginCompleteDelegates(LocalUserNum, true, *FUniqueNetIdSteam::Create(SteamUserPtr->GetSteamID()), TEXT("")); return true; } TriggerOnLoginCompleteDelegates(0, false, *FUniqueNetIdSteam::EmptyId(), TEXT("AutoLogin failed. Not logged in or no connection.")); return false; } else { // Autologin for dedicated servers happens via session creation in the GameServerAPI LogOnAnonymous() return false; } } ELoginStatus::Type FOnlineIdentitySteam::GetLoginStatus(int32 LocalUserNum) const { if (LocalUserNum < MAX_LOCAL_PLAYERS && SteamUserPtr != NULL) { return SteamUserPtr->BLoggedOn() ? ELoginStatus::LoggedIn : ELoginStatus::NotLoggedIn; } return ELoginStatus::NotLoggedIn; } ELoginStatus::Type FOnlineIdentitySteam::GetLoginStatus(const FUniqueNetId& UserId) const { return GetLoginStatus(0); } FUniqueNetIdPtr FOnlineIdentitySteam::GetUniquePlayerId(int32 LocalUserNum) const { if (LocalUserNum < MAX_LOCAL_PLAYERS && SteamUserPtr != NULL) { return FUniqueNetIdSteam::Create(SteamUserPtr->GetSteamID()); } return NULL; } FUniqueNetIdPtr FOnlineIdentitySteam::CreateUniquePlayerId(uint8* Bytes, int32 Size) { if (Bytes && Size == sizeof(uint64)) { uint64* RawUniqueId = (uint64*)Bytes; CSteamID SteamId(*RawUniqueId); if (SteamId.IsValid()) { return FUniqueNetIdSteam::Create(SteamId); } } return NULL; } FUniqueNetIdPtr FOnlineIdentitySteam::CreateUniquePlayerId(const FString& Str) { return FUniqueNetIdSteam::Create(Str); } /** * Reads the player's nick name from the online service * * @param LocalUserNum the controller number of the associated user * * @return a string containing the players nick name */ FString FOnlineIdentitySteam::GetPlayerNickname(int32 LocalUserNum) const { if (LocalUserNum < MAX_LOCAL_PLAYERS && SteamFriendsPtr != NULL) { const char* PersonaName = SteamFriendsPtr->GetPersonaName(); return FString(UTF8_TO_TCHAR(PersonaName)); } return FString(TEXT("")); } FString FOnlineIdentitySteam::GetPlayerNickname(const FUniqueNetId& UserId) const { const FUniqueNetIdSteam& SteamId = (const FUniqueNetIdSteam&)UserId; if (SteamFriendsPtr != NULL) { FString PersonaName = UTF8_TO_TCHAR(SteamFriendsPtr->GetFriendPersonaName(SteamId)); return PersonaName; } return FString(TEXT("")); } /** * Gets a user's platform specific authentication token to verify their identity * * @param LocalUserNum the controller number of the associated user * * @return String representing the authentication token */ FString FOnlineIdentitySteam::GetAuthToken(int32 LocalUserNum) const { FString ResultToken; if (LocalUserNum < MAX_LOCAL_PLAYERS) { // Double check they are properly logged in if (SteamUserPtr != NULL && SteamUserPtr->BLoggedOn()) { uint8 AuthToken[1024]; uint32 AuthTokenSize = 0; if (SteamUserPtr->GetAuthSessionTicket(AuthToken, UE_ARRAY_COUNT(AuthToken), &AuthTokenSize, nullptr) != k_HAuthTicketInvalid && AuthTokenSize > 0) { ResultToken = BytesToHex(AuthToken, AuthTokenSize); UE_LOG_ONLINE_IDENTITY(Log, TEXT("Obtained steam authticket")); // In release builds our code checks the authTicket faster than Steam's login server can save it // Added a small amount of sleep here so the ResultToken is valid by the time this call returns FPlatformProcess::Sleep(0.1f); } else { UE_LOG_ONLINE_IDENTITY(Warning, TEXT("Failed to acquire Steam auth session ticket for %d"), LocalUserNum); } } } return ResultToken; } void FOnlineIdentitySteam::RevokeAuthToken(const FUniqueNetId& UserId, const FOnRevokeAuthTokenCompleteDelegate& Delegate) { UE_LOG_ONLINE_IDENTITY(Display, TEXT("FOnlineIdentitySteam::RevokeAuthToken not implemented")); FUniqueNetIdRef UserIdRef(UserId.AsShared()); SteamSubsystem->ExecuteNextTick([UserIdRef, Delegate]() { Delegate.ExecuteIfBound(*UserIdRef, FOnlineError(FString(TEXT("RevokeAuthToken not implemented")))); }); } void FOnlineIdentitySteam::GetUserPrivilege(const FUniqueNetId& UserId, EUserPrivileges::Type Privilege, const FOnGetUserPrivilegeCompleteDelegate& Delegate, EShowPrivilegeResolveUI ShowResolveUI) { Delegate.ExecuteIfBound(UserId, Privilege, (uint32)EPrivilegeResults::NoFailures); } FPlatformUserId FOnlineIdentitySteam::GetPlatformUserIdFromUniqueNetId(const FUniqueNetId& UniqueNetId) const { for (int i = 0; i < MAX_LOCAL_PLAYERS; ++i) { auto CurrentUniqueId = GetUniquePlayerId(i); if (CurrentUniqueId.IsValid() && (*CurrentUniqueId == UniqueNetId)) { return GetPlatformUserIdFromLocalUserNum(i); } } return PLATFORMUSERID_NONE; } FString FOnlineIdentitySteam::GetAuthType() const { return TEXT(""); } void FOnlineIdentitySteam::GetLinkedAccountAuthToken(int32 LocalUserNum, const FString& InTokenType, const FOnGetLinkedAccountAuthTokenCompleteDelegate& Delegate) const { const TCHAR* AppTokenType = TEXT("App"); const TCHAR* SessionTokenType = TEXT("Session"); const TCHAR* WebAPITokenType = TEXT("WebAPI"); FString TokenType = InTokenType; if (TokenType.IsEmpty()) { // Default to APP tokens TokenType = AppTokenType; // But allow the user to configure something else GConfig->GetString(TEXT("OnlineSubsystemSteam"), TEXT("DefaultLinkedAccountAuthTokenType"), TokenType, GEngineIni); } if (TokenType == AppTokenType) { SteamSubsystem->GetEncryptedAppTicketInterface()->OnEncryptedAppTicketResultDelegate.AddLambda([this, LocalUserNum, OnComplete = FOnGetLinkedAccountAuthTokenCompleteDelegate(Delegate)](bool bEncryptedDataAvailable, int32 ResultCode) { FExternalAuthToken ExternalToken; if (bEncryptedDataAvailable) { SteamSubsystem->GetEncryptedAppTicketInterface()->GetEncryptedAppTicket(ExternalToken.TokenData); } // Pass the info back to the original caller OnComplete.ExecuteIfBound(LocalUserNum, ExternalToken.HasTokenData(), ExternalToken); }); SteamSubsystem->GetEncryptedAppTicketInterface()->RequestEncryptedAppTicket(nullptr, 0); } else if (TokenType == SessionTokenType) { FExternalAuthToken AuthToken; AuthToken.TokenString = GetAuthToken(LocalUserNum); Delegate.ExecuteIfBound(LocalUserNum, AuthToken.HasTokenString(), AuthToken); } else if (TokenType.StartsWith(WebAPITokenType)) { if (SteamUserPtr != NULL && SteamUserPtr->BLoggedOn()) { // Split on ':' to allow client to specify web service; if not set use default specified in config FString RemoteServiceIdentity; if (!TokenType.Split(TEXT(":"), nullptr, &RemoteServiceIdentity)) { GConfig->GetString(TEXT("OnlineSubsystemSteam"), TEXT("DefaultRemoteServiceIdentity"), RemoteServiceIdentity, GEngineIni); } if (RemoteServiceIdentity.IsEmpty()) { UE_LOG_ONLINE_IDENTITY(Warning, TEXT("FOnlineIdentitySteam::GetLinkedAccountAuthToken DefaultRemoteServiceIdentity not set")); Delegate.ExecuteIfBound(LocalUserNum, false, FExternalAuthToken()); return; } SteamSubsystem->GetAuthInterface()->GetAuthTicketForWebApi(RemoteServiceIdentity, FOnGetAuthTicketForWebApiCompleteDelegate::CreateLambda( [this, LocalUserNum, OnComplete = Delegate](uint32 AuthTicketHandle, const FString& TicketString) { FExternalAuthToken AuthToken; AuthToken.TokenString = TicketString; OnComplete.ExecuteIfBound(LocalUserNum, AuthToken.HasTokenString(), AuthToken); })); } } else { UE_LOG_ONLINE_IDENTITY(Warning, TEXT("FOnlineIdentitySteam::GetLinkedAccountAuthToken TokenType=[%s] unknown"), *TokenType); Delegate.ExecuteIfBound(LocalUserNum, false, FExternalAuthToken()); } }