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

381 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "OnlineIdentityNull.h"
#include "Misc/CommandLine.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/Guid.h"
#include "Misc/OutputDeviceRedirector.h"
#include "OnlineSubsystemNull.h"
#include "SocketSubsystem.h"
#include "OnlineError.h"
#define USER_ATTR_NULL_LOGINCOUNT TEXT("null:logincount")
bool FUserOnlineAccountNull::GetAuthAttribute(const FString& AttrName, FString& OutAttrValue) const
{
const FString* FoundAttr = AdditionalAuthData.Find(AttrName);
if (FoundAttr != NULL)
{
OutAttrValue = *FoundAttr;
return true;
}
return false;
}
bool FUserOnlineAccountNull::GetUserAttribute(const FString& AttrName, FString& OutAttrValue) const
{
const FString* FoundAttr = UserAttributes.Find(AttrName);
if (FoundAttr != NULL)
{
OutAttrValue = *FoundAttr;
return true;
}
return false;
}
bool FUserOnlineAccountNull::SetUserAttribute(const FString& AttrName, const FString& AttrValue)
{
const FString* FoundAttr = UserAttributes.Find(AttrName);
if (FoundAttr == NULL || *FoundAttr != AttrValue)
{
UserAttributes.Add(AttrName, AttrValue);
return true;
}
return false;
}
FString FOnlineIdentityNull::GenerateRandomUserId(int32 LocalUserNum)
{
FString HostName;
if (!ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetHostName(HostName))
{
// could not get hostname, use address
bool bCanBindAll;
TSharedPtr<class FInternetAddr> Addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLocalHostAddr(*GLog, bCanBindAll);
HostName = Addr->ToString(false);
}
bool bUseStableNullId = FOnlineSubsystemNull::bForceStableNullId;
FString UserSuffix;
if (FOnlineSubsystemNull::bAddUserNumToNullId)
{
UserSuffix = FString::Printf(TEXT("-%d"), LocalUserNum);
}
if (FPlatformProcess::IsFirstInstance() && !GIsEditor)
{
// If we're outside the editor and know this is the first instance, use the system login id
bUseStableNullId = true;
}
if (bUseStableNullId)
{
// Use a stable id possibly with a user num suffix
return FString::Printf(TEXT( "%s-%s%s" ), *HostName, *FPlatformMisc::GetLoginId().ToUpper(), *UserSuffix);
}
// If we're not the first instance (or in the editor), return truly random id
return FString::Printf(TEXT( "%s-%s%s" ), *HostName, *FGuid::NewGuid().ToString(), *UserSuffix);
}
bool FOnlineIdentityNull::Login(int32 LocalUserNum, const FOnlineAccountCredentials& AccountCredentials)
{
return LoginInternal(LocalUserNum, AccountCredentials, FOnLoginCompleteDelegate::CreateLambda(
[this](int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& ErrorStr)
{
TriggerOnLoginCompleteDelegates(LocalUserNum, bWasSuccessful, UserId, ErrorStr);
}));
}
bool FOnlineIdentityNull::LoginInternal(int32 LocalUserNum, const FOnlineAccountCredentials& AccountCredentials, const FOnLoginCompleteDelegate& InCompletionDelegate)
{
FString ErrorStr;
TSharedPtr<FUserOnlineAccountNull> UserAccountPtr;
// valid local player index
if (LocalUserNum < 0 || LocalUserNum >= MAX_LOCAL_PLAYERS)
{
ErrorStr = FString::Printf(TEXT("Invalid LocalUserNum=%d"), LocalUserNum);
}
else if (FOnlineSubsystemNull::bRequireShowLoginUI && !AccountCredentials.Type.Contains(TEXT("LoginUI")))
{
ErrorStr = TEXT("Only supports login using ShowLoginUI");
}
else if (FOnlineSubsystemNull::bRequireLoginCredentials && AccountCredentials.Id.IsEmpty())
{
ErrorStr = TEXT("Invalid account id, string empty");
}
else
{
FUniqueNetIdPtr* UserId = UserIds.Find(LocalUserNum);
if (UserId == nullptr)
{
FString RandomUserId = GenerateRandomUserId(LocalUserNum);
const FUniqueNetIdRef NewUserId = FUniqueNetIdNull::Create(RandomUserId);
UserAccountPtr = MakeShareable(new FUserOnlineAccountNull(RandomUserId));
UserAccountPtr->UserAttributes.Add(USER_ATTR_ID, RandomUserId);
// update/add cached entry for user
UserAccounts.Add(NewUserId, UserAccountPtr.ToSharedRef());
// keep track of user ids for local users
UserIds.Add(LocalUserNum, UserAccountPtr->GetUserId());
}
else
{
UserAccountPtr = UserAccounts.FindChecked(UserId->ToSharedRef());
}
// Increment the login count
FString LoginCountString = TEXT("0");
UserAccountPtr->GetUserAttribute(USER_ATTR_NULL_LOGINCOUNT, LoginCountString);
int32 LoginCount = FCString::Atoi(*LoginCountString) + 1;
LoginCountString = FString::Printf(TEXT("%d"), LoginCount);
UserAccountPtr->SetUserAttribute(USER_ATTR_NULL_LOGINCOUNT, LoginCountString);
}
if (!ErrorStr.IsEmpty())
{
UE_LOG_ONLINE_IDENTITY(Warning, TEXT("Login request failed. %s"), *ErrorStr);
InCompletionDelegate.Execute(LocalUserNum, false, *FUniqueNetIdNull::EmptyId(), ErrorStr);
return false;
}
InCompletionDelegate.Execute(LocalUserNum, true, *UserAccountPtr->GetUserId(), ErrorStr);
return true;
}
bool FOnlineIdentityNull::Logout(int32 LocalUserNum)
{
FUniqueNetIdPtr UserId = GetUniquePlayerId(LocalUserNum);
if (UserId.IsValid())
{
// remove cached user account
UserAccounts.Remove(UserId.ToSharedRef());
// remove cached user id
UserIds.Remove(LocalUserNum);
// not async but should call completion delegate anyway
TriggerOnLogoutCompleteDelegates(LocalUserNum, true);
return true;
}
else
{
UE_LOG_ONLINE_IDENTITY(Warning, TEXT("No logged in user found for LocalUserNum=%d."),
LocalUserNum);
TriggerOnLogoutCompleteDelegates(LocalUserNum, false);
}
return false;
}
bool FOnlineIdentityNull::AutoLogin(int32 LocalUserNum)
{
FString LoginStr;
FString PasswordStr;
FString TypeStr;
FParse::Value(FCommandLine::Get(), TEXT("AUTH_LOGIN="), LoginStr);
FParse::Value(FCommandLine::Get(), TEXT("AUTH_PASSWORD="), PasswordStr);
FParse::Value(FCommandLine::Get(), TEXT("AUTH_TYPE="), TypeStr);
bool bEnableWarning = LoginStr.Len() > 0 || PasswordStr.Len() > 0 || TypeStr.Len() > 0;
if (!LoginStr.IsEmpty())
{
if (!PasswordStr.IsEmpty())
{
if (!TypeStr.IsEmpty())
{
return Login(LocalUserNum, FOnlineAccountCredentials(TypeStr, LoginStr, PasswordStr));
}
else if (bEnableWarning)
{
UE_LOG_ONLINE_IDENTITY(Warning, TEXT("AutoLogin missing AUTH_TYPE=<type>."));
}
}
else if (bEnableWarning)
{
UE_LOG_ONLINE_IDENTITY(Warning, TEXT("AutoLogin missing AUTH_PASSWORD=<password>."));
}
}
else if (!FOnlineSubsystemNull::bRequireLoginCredentials)
{
// Act like a console and login with empty auth
return Login(LocalUserNum, FOnlineAccountCredentials());
}
else if (bEnableWarning)
{
UE_LOG_ONLINE_IDENTITY(Warning, TEXT("AutoLogin missing AUTH_LOGIN=<login id>."));
}
return false;
}
TSharedPtr<FUserOnlineAccount> FOnlineIdentityNull::GetUserAccount(const FUniqueNetId& UserId) const
{
TSharedPtr<FUserOnlineAccount> Result;
if (const TSharedRef<FUserOnlineAccountNull>* FoundUserAccount = UserAccounts.Find(UserId.AsShared()))
{
Result = *FoundUserAccount;
}
return Result;
}
TArray<TSharedPtr<FUserOnlineAccount> > FOnlineIdentityNull::GetAllUserAccounts() const
{
TArray<TSharedPtr<FUserOnlineAccount> > Result;
for (TUniqueNetIdMap<TSharedRef<FUserOnlineAccountNull>>::TConstIterator It(UserAccounts); It; ++It)
{
Result.Add(It.Value());
}
return Result;
}
FUniqueNetIdPtr FOnlineIdentityNull::GetUniquePlayerId(int32 LocalUserNum) const
{
const FUniqueNetIdPtr* FoundId = UserIds.Find(LocalUserNum);
if (FoundId != NULL)
{
return *FoundId;
}
return NULL;
}
FUniqueNetIdPtr FOnlineIdentityNull::CreateUniquePlayerId(uint8* Bytes, int32 Size)
{
if (Bytes != NULL && Size > 0)
{
FString StrId = FString::ConstructFromPtrSize((TCHAR*)Bytes, Size);
return FUniqueNetIdNull::Create(StrId);
}
return NULL;
}
FUniqueNetIdPtr FOnlineIdentityNull::CreateUniquePlayerId(const FString& Str)
{
return FUniqueNetIdNull::Create(Str);
}
ELoginStatus::Type FOnlineIdentityNull::GetLoginStatus(int32 LocalUserNum) const
{
FUniqueNetIdPtr UserId = GetUniquePlayerId(LocalUserNum);
if (UserId.IsValid())
{
return GetLoginStatus(*UserId);
}
return ELoginStatus::NotLoggedIn;
}
ELoginStatus::Type FOnlineIdentityNull::GetLoginStatus(const FUniqueNetId& UserId) const
{
TSharedPtr<FUserOnlineAccount> UserAccount = GetUserAccount(UserId);
if (UserAccount.IsValid() &&
UserAccount->GetUserId()->IsValid())
{
if (FOnlineSubsystemNull::bForceOfflineMode)
{
return ELoginStatus::UsingLocalProfile;
}
else if (FOnlineSubsystemNull::bOnlineRequiresSecondLogin)
{
FString LoginCountString = TEXT("0");
UserAccount->GetUserAttribute(USER_ATTR_NULL_LOGINCOUNT, LoginCountString);
int32 LoginCount = FCString::Atoi(*LoginCountString);
if (LoginCount < 2)
{
return ELoginStatus::UsingLocalProfile;
}
}
return ELoginStatus::LoggedIn;
}
return ELoginStatus::NotLoggedIn;
}
FString FOnlineIdentityNull::GetPlayerNickname(int32 LocalUserNum) const
{
FUniqueNetIdPtr UniqueId = GetUniquePlayerId(LocalUserNum);
if (UniqueId.IsValid())
{
return UniqueId->ToString();
}
return TEXT("NullUser");
}
FString FOnlineIdentityNull::GetPlayerNickname(const FUniqueNetId& UserId) const
{
return UserId.ToString();
}
FString FOnlineIdentityNull::GetAuthToken(int32 LocalUserNum) const
{
FUniqueNetIdPtr UserId = GetUniquePlayerId(LocalUserNum);
if (UserId.IsValid())
{
TSharedPtr<FUserOnlineAccount> UserAccount = GetUserAccount(*UserId);
if (UserAccount.IsValid())
{
return UserAccount->GetAccessToken();
}
}
return FString();
}
void FOnlineIdentityNull::RevokeAuthToken(const FUniqueNetId& UserId, const FOnRevokeAuthTokenCompleteDelegate& Delegate)
{
UE_LOG_ONLINE_IDENTITY(Display, TEXT("FOnlineIdentityNull::RevokeAuthToken not implemented"));
FUniqueNetIdRef UserIdRef(UserId.AsShared());
NullSubsystem->ExecuteNextTick([UserIdRef, Delegate]()
{
Delegate.ExecuteIfBound(*UserIdRef, FOnlineError(FString(TEXT("RevokeAuthToken not implemented"))));
});
}
FOnlineIdentityNull::FOnlineIdentityNull(FOnlineSubsystemNull* InSubsystem)
: NullSubsystem(InSubsystem)
{
if (FOnlineSubsystemNull::bAutoLoginAtStartup)
{
// autologin the 0-th player
Login(0, FOnlineAccountCredentials(TEXT("DummyType"), TEXT("DummyUser"), TEXT("DummyId")) );
}
}
FOnlineIdentityNull::~FOnlineIdentityNull()
{
}
void FOnlineIdentityNull::GetUserPrivilege(const FUniqueNetId& UserId, EUserPrivileges::Type Privilege, const FOnGetUserPrivilegeCompleteDelegate& Delegate, EShowPrivilegeResolveUI ShowResolveUI)
{
if (FOnlineSubsystemNull::bForceOfflineMode && Privilege == EUserPrivileges::CanPlayOnline)
{
Delegate.ExecuteIfBound(UserId, Privilege, (uint32)EPrivilegeResults::NetworkConnectionUnavailable);
}
Delegate.ExecuteIfBound(UserId, Privilege, (uint32)EPrivilegeResults::NoFailures);
}
FPlatformUserId FOnlineIdentityNull::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 FOnlineIdentityNull::GetAuthType() const
{
return TEXT("");
}