Files
UnrealEngine/Engine/Plugins/Online/OnlineSubsystemSteam/Source/Private/OnlineIdentityInterfaceSteam.cpp
2025-05-18 13:04:45 +08:00

317 lines
9.8 KiB
C++

// 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 <steam/isteamuser.h>
FOnlineIdentitySteam::FOnlineIdentitySteam(FOnlineSubsystemSteam* InSubsystem) :
SteamUserPtr(NULL),
SteamFriendsPtr(NULL),
SteamSubsystem(InSubsystem)
{
SteamUserPtr = SteamUser();
SteamFriendsPtr = SteamFriends();
}
TSharedPtr<FUserOnlineAccount> FOnlineIdentitySteam::GetUserAccount(const FUniqueNetId& UserId) const
{
//@todo - not implemented
return NULL;
}
TArray<TSharedPtr<FUserOnlineAccount> > FOnlineIdentitySteam::GetAllUserAccounts() const
{
//@todo - not implemented
return TArray<TSharedPtr<FUserOnlineAccount> >();
}
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());
}
}