|
#include "LoginScene.h" |
|
#include "Components/Button.h" |
|
#include "Components/VerticalBox.h" |
|
#include "Components/VerticalBoxSlot.h" |
|
#include "Components/TextBlock.h" |
|
#include "Online/OnlineServices.h" |
|
#include "Online/Auth.h" |
|
#include "Online/OnlineAsyncOp.h" |
|
#include "Online/OnlineIdEOSGS.h" |
|
#include "IEOSSDKManager.h" |
|
#include "EOSShared.h" |
|
|
|
THIRD_PARTY_INCLUDES_START |
|
#include "eos_sdk.h" |
|
THIRD_PARTY_INCLUDES_END |
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogSteamEOS, Log, All); |
|
|
|
#define EOSLOG(Verbosity, Message, ...) AddToLog(ELogVerbosity::Verbosity, FString::Printf(Message, ##__VA_ARGS__)); |
|
|
|
void ULoginScene::NativeConstruct() |
|
{ |
|
Super::NativeConstruct(); |
|
|
|
BTN_Login->OnClicked.AddDynamic(this, &ULoginScene::OnLoginClicked); |
|
EOSLOG(Log, TEXT("LoginScene ready")); |
|
|
|
// Already done by 'SteamShared' module, just for check if Steamworks is usable |
|
bSteamEnabled = SteamAPI_Init(); |
|
EOSLOG(Log, TEXT("Steam enabled: %s"), *LexToString(bSteamEnabled)); |
|
} |
|
|
|
void ULoginScene::NativeDestruct() |
|
{ |
|
CallbackGetTicketForWebApi.Unregister(); |
|
|
|
// Processed by 'SteamShared', would't do here |
|
// SteamAPI_Shutdown(); |
|
|
|
Super::NativeDestruct(); |
|
} |
|
|
|
void ULoginScene::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) |
|
{ |
|
Super::NativeTick(MyGeometry, InDeltaTime); |
|
|
|
// Pump Steamworks API Callbacks |
|
SteamAPI_RunCallbacks(); |
|
} |
|
|
|
void ULoginScene::OnLoginClicked() |
|
{ |
|
if (!bSteamEnabled) |
|
{ |
|
EOSLOG(Error, TEXT("Steam is not running")); |
|
return; |
|
} |
|
|
|
if (AuthTicketHandle != 0) |
|
{ |
|
EOSLOG(Error, TEXT("Previous request is not completed")); |
|
return; |
|
} |
|
|
|
// Register callback |
|
CallbackGetTicketForWebApi.Register(this, &ULoginScene::OnGetAuthTicketForWebApiCompleted); |
|
|
|
constexpr char ApiTarget[] = "epiconlineservices"; |
|
AuthTicketHandle = SteamUser()->GetAuthTicketForWebApi(ApiTarget); |
|
|
|
EOSLOG(Log, TEXT("Login clicked, Steam auth ticket handle: 0x%08X"), AuthTicketHandle); |
|
} |
|
|
|
void ULoginScene::OnGetAuthTicketForWebApiCompleted(GetTicketForWebApiResponse_t* Response) |
|
{ |
|
if (Response->m_hAuthTicket != AuthTicketHandle) |
|
{ |
|
EOSLOG(Error, TEXT("Auth ticket handle mismatch: 0x%08X != 0x%08X"), Response->m_hAuthTicket, AuthTicketHandle); |
|
return; |
|
} |
|
|
|
AuthTicketHandle = 0; |
|
|
|
if (Response->m_eResult != k_EResultOK) |
|
{ |
|
EOSLOG(Error, TEXT("Failed to get auth ticket for web api: %d"), Response->m_eResult); |
|
return; |
|
} |
|
|
|
FString TokenString = FString::FromHexBlob(Response->m_rgubTicket, Response->m_cubTicket); |
|
EOSLOG(Log, TEXT("Steam Auth ticket for web api received: %s"), *TokenString); |
|
|
|
StartLoginEOS(TokenString); |
|
} |
|
|
|
void ULoginScene::StartLoginEOS(const FString& SteamTicket) |
|
{ |
|
EOSLOG(Log, TEXT("Starting EOS login with Steam ticket: %s"), *SteamTicket); |
|
|
|
UE::Online::IOnlineServicesPtr OnlineServices = UE::Online::GetServices(UE::Online::EOnlineServices::Epic); |
|
// UE::Online::IOnlineServicesPtr OnlineServices = UE::Online::GetServices(); |
|
if (!OnlineServices.IsValid()) |
|
{ |
|
EOSLOG(Error, TEXT("OnlineServices is not valid")); |
|
return; |
|
} |
|
|
|
UE::Online::IAuthPtr AuthInterface = OnlineServices->GetAuthInterface(); |
|
if (!AuthInterface.IsValid()) |
|
{ |
|
EOSLOG(Error, TEXT("AuthInterface is not valid")); |
|
return; |
|
} |
|
|
|
ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); |
|
if (!LocalPlayer) |
|
{ |
|
EOSLOG(Error, TEXT("No local player found")); |
|
return; |
|
} |
|
|
|
UE::Online::FAuthLogin::Params LoginParams; |
|
LoginParams.PlatformUserId = LocalPlayer->GetPlatformUserId(); |
|
LoginParams.CredentialsType = UE::Online::LoginCredentialsType::ExternalAuth; |
|
LoginParams.CredentialsToken.Set<UE::Online::FExternalAuthToken>( |
|
{ |
|
.Type = UE::Online::ExternalLoginType::SteamSessionTicket, |
|
.Data = SteamTicket, |
|
}); |
|
|
|
AuthInterface->Login(MoveTemp(LoginParams)).OnComplete(this, &ULoginScene::OnEOSLoginCompleted); |
|
} |
|
|
|
void ULoginScene::OnEOSLoginCompleted(const UE::Online::TOnlineResult<UE::Online::FAuthLogin>& Result) |
|
{ |
|
if (!Result.IsOk()) |
|
{ |
|
EOSLOG(Error, TEXT("EOS login failed: %s"), *Result.GetErrorValue().GetText().ToString()); |
|
return; |
|
} |
|
|
|
const static FString TargetPlatformName = TEXT("OnlineServices.EOS"); |
|
IEOSPlatformHandlePtr EpicPlatform; |
|
for (const IEOSPlatformHandlePtr& ActivePlatform : IEOSSDKManager::Get()->GetActivePlatforms()) |
|
{ |
|
if (ActivePlatform->GetConfigName() == TargetPlatformName) |
|
{ |
|
EpicPlatform = ActivePlatform; |
|
break; |
|
} |
|
} |
|
|
|
if (!EpicPlatform.IsValid()) |
|
{ |
|
EOSLOG(Error, TEXT("Target platform not found: %s"), *TargetPlatformName); |
|
return; |
|
} |
|
|
|
EOS_HPlatform EpicPlatformHandle = *EpicPlatform; |
|
if (EpicPlatformHandle == nullptr) |
|
{ |
|
EOSLOG(Error, TEXT("Epic platform handle is null")); |
|
return; |
|
} |
|
|
|
const UE::Online::FAccountId AccountId = Result.GetOkValue().AccountInfo->AccountId; |
|
EOS_HConnect ConnectHandle = EOS_Platform_GetConnectInterface(EpicPlatformHandle); |
|
EOS_Connect_CopyIdTokenOptions CopyIdTokenOptions = |
|
{ |
|
.ApiVersion = EOS_CONNECT_COPYIDTOKEN_API_LATEST, |
|
.LocalUserId = UE::Online::GetProductUserIdChecked(AccountId), |
|
}; |
|
|
|
EOS_Connect_IdToken* IdToken = nullptr; |
|
EOS_EResult CopyIdTokenResult = EOS_Connect_CopyIdToken(ConnectHandle, &CopyIdTokenOptions, &IdToken); |
|
if (CopyIdTokenResult != EOS_EResult::EOS_Success) |
|
{ |
|
EOSLOG(Error, TEXT("Failed to copy id token: %s"), *LexToString(CopyIdTokenResult)); |
|
return; |
|
} |
|
|
|
// We got the token |
|
// The token can be used later for further authentication on server side. |
|
FString Token = UTF8_TO_TCHAR(IdToken->JsonWebToken); |
|
EOSLOG(Log, TEXT("EOS login successful: %s"), *Token); |
|
} |
|
|
|
// Utility function for log visualization |
|
void ULoginScene::AddToLog(ELogVerbosity::Type Verbosity, const FString& Message) |
|
{ |
|
constexpr FLinearColor ColorLog = FLinearColor(1, 1, 1, 1); |
|
constexpr FLinearColor ColorWarning = FLinearColor(1, 1, 0, 1); |
|
constexpr FLinearColor ColorError = FLinearColor(1, 0, 0, 1); |
|
|
|
float Time = GetWorld()->GetTimeSeconds(); |
|
|
|
FString FormattedMessage = FString::Printf(TEXT("%.2f: %s"), Time, *Message); |
|
UTextBlock* TextBlock = NewObject<UTextBlock>(this); |
|
TextBlock->SetText(FText::FromString(FormattedMessage)); |
|
|
|
switch (Verbosity) |
|
{ |
|
case ELogVerbosity::Warning: |
|
TextBlock->SetColorAndOpacity(ColorWarning); |
|
break; |
|
case ELogVerbosity::Error: |
|
TextBlock->SetColorAndOpacity(ColorError); |
|
break; |
|
default: |
|
TextBlock->SetColorAndOpacity(ColorLog); |
|
break; |
|
} |
|
|
|
UVerticalBoxSlot* NewSlot = VB_Log->AddChildToVerticalBox(TextBlock); |
|
NewSlot->SetVerticalAlignment(EVerticalAlignment::VAlign_Center); |
|
NewSlot->SetHorizontalAlignment(EHorizontalAlignment::HAlign_Fill); |
|
NewSlot->SetPadding(FMargin(0, 0, 0, 0)); |
|
|
|
UE_LOG(LogSteamEOS, Log, TEXT("%s"), *Message); |
|
} |