diff --git a/Source/Immutable/Private/Immutable/ImmutablePassport.cpp b/Source/Immutable/Private/Immutable/ImmutablePassport.cpp index af1aff1..be79124 100644 --- a/Source/Immutable/Private/Immutable/ImmutablePassport.cpp +++ b/Source/Immutable/Private/Immutable/ImmutablePassport.cpp @@ -6,6 +6,8 @@ #include "Immutable/ImmutableResponses.h" #include "ImtblJSConnector.h" #include "JsonObjectConverter.h" +#include "Immutable/ImmutableSaveGame.h" +#include "Kismet/GameplayStatics.h" #include "Policies/CondensedJsonPrintPolicy.h" @@ -23,6 +25,8 @@ #include "Mac/ImmutableMac.h" #endif +#define PASSPORT_SAVE_GAME_SLOT_NAME TEXT("ImmutablePassport") + FString FImmutablePassportInitData::ToJsonString() const { FString OutString; @@ -141,7 +145,7 @@ void UImmutablePassport::Initialize(const FImmutablePassportInitData& Data, check(JSConnector.IsValid()); InitData = Data; - + CallJS(ImmutablePassportAction::INIT, InitData.ToJsonString(), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnInitializeResponse), false); } @@ -168,7 +172,7 @@ void UImmutablePassport::Connect(bool IsConnectImx, bool TryToRelogin, const FIm #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC void UImmutablePassport::ConnectPKCE(bool IsConnectImx, const FImtblPassportResponseDelegate &ResponseDelegate) { - SetStateFlags(IPS_CONNECTING | IPS_PKCE); + SetStateFlags(IPS_CONNECTING|IPS_PKCE); if (IsConnectImx) { SetStateFlags(IPS_IMX); @@ -182,19 +186,19 @@ void UImmutablePassport::ConnectPKCE(bool IsConnectImx, const FImtblPassportResp void UImmutablePassport::Logout(const FImtblPassportResponseDelegate& ResponseDelegate) { #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC - if (IsStateFlagSet(IPS_PKCE)) + if (IsStateFlagsSet(IPS_PKCE) || bIsPrevConnectedViaPKCEFlow) { PKCELogoutResponseDelegate = ResponseDelegate; } #endif - if (IsStateFlagSet(IPS_CONNECTED)) + if (IsStateFlagsSet(IPS_CONNECTED)) { CallJS(ImmutablePassportAction::Logout, TEXT(""), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnLogoutResponse)); } else { - IMTBL_ERR("Passport is not connected to execute logout."); + IMTBL_WARN("Passport is not connected to execute logout."); } } @@ -228,7 +232,7 @@ void UImmutablePassport::ConfirmCode(const FString& DeviceCode, const float Inte const FImtblPassportResponseDelegate& ResponseDelegate) { FImmutablePassportCodeConfirmRequestData Data { DeviceCode, Interval }; - FString Action = IsStateFlagSet(IPS_IMX) ? ImmutablePassportAction::CONNECT_CONFIRM_CODE : ImmutablePassportAction::LOGIN_CONFIRM_CODE; + FString Action = IsStateFlagsSet(IPS_IMX) ? ImmutablePassportAction::CONNECT_CONFIRM_CODE : ImmutablePassportAction::LOGIN_CONFIRM_CODE; CallJS(Action, UStructToJsonString(Data), ResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnConfirmCodeResponse)); } @@ -371,7 +375,7 @@ void UImmutablePassport::ReinstateConnection(FImtblJSResponse Response) bool UImmutablePassport::CheckIsInitialized(const FString& Action, const FImtblPassportResponseDelegate& ResponseDelegate) const { - const bool IsInitialized = IsStateFlagSet(IPS_INITIALIZED); + const bool IsInitialized = IsStateFlagsSet(IPS_INITIALIZED); if (!IsInitialized) { @@ -415,6 +419,8 @@ void UImmutablePassport::OnInitializeResponse(FImtblJSResponse Response) { SetStateFlags(IPS_INITIALIZED); IMTBL_LOG("Passport initialization succeeded.") + // we load settings in case if player has not logged out properly + LoadPassportSettings(); } else { @@ -472,7 +478,7 @@ void UImmutablePassport::OnLogoutResponse(FImtblJSResponse Response) if (!Url.IsEmpty()) { #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC - if (IsStateFlagSet(IPS_PKCE)) + if (IsStateFlagsSet(IPS_PKCE) || bIsPrevConnectedViaPKCEFlow) { OnHandleDeepLink = FImtblPassportHandleDeepLinkDelegate::CreateUObject(this, &UImmutablePassport::OnDeepLinkActivated); #if PLATFORM_ANDROID @@ -610,6 +616,11 @@ void UImmutablePassport::OnConnectPKCEResponse(FImtblJSResponse Response) } PKCEResponseDelegate.ExecuteIfBound(FImmutablePassportResult{Response.success, Msg}); PKCEResponseDelegate = nullptr; + + // we save passport state for PKCE flow in case if we decide to close a game + // and reopen it later with Passport is still being connected and we decided to logout. + // In this case, we logout using PKCE flow + SavePassportSettings(); } else { @@ -775,7 +786,7 @@ void UImmutablePassport::OnConfirmCodeResponse(FImtblJSResponse Response) if (auto ResponseDelegate = GetResponseDelegate(Response)) { FString Msg; - FString TypeOfConnection = IsStateFlagSet(IPS_IMX) ? TEXT("connect") : TEXT("login"); + FString TypeOfConnection = IsStateFlagsSet(IPS_IMX) ? TEXT("connect") : TEXT("login"); ResetStateFlags(IPS_CONNECTING); if (Response.success) @@ -927,11 +938,35 @@ void UImmutablePassport::ResetStateFlags(uint8 StateIn) StateFlags &= ~StateIn; } -bool UImmutablePassport::IsStateFlagSet(uint8 StateIn) const +bool UImmutablePassport::IsStateFlagsSet(uint8 StateIn) const { return (StateFlags & StateIn) == StateIn; } +void UImmutablePassport::SavePassportSettings() +{ + UImmutableSaveGame* SaveGameInstance = Cast(UGameplayStatics::CreateSaveGameObject(UImmutableSaveGame::StaticClass())); + + SaveGameInstance->bWasConnectedViaPKCEFlow = IsStateFlagsSet(IPS_PKCE|IPS_CONNECTED); + + UGameplayStatics::SaveGameToSlot(SaveGameInstance, PASSPORT_SAVE_GAME_SLOT_NAME, 0); +} + +void UImmutablePassport::LoadPassportSettings() +{ + UImmutableSaveGame* SaveGameInstance = Cast(UGameplayStatics::CreateSaveGameObject(UImmutableSaveGame::StaticClass())); + + SaveGameInstance = Cast(UGameplayStatics::LoadGameFromSlot(PASSPORT_SAVE_GAME_SLOT_NAME, 0)); + + if (!SaveGameInstance) + { + IMTBL_ERR("Could not find Immutable save game to load") + return; + } + + bIsPrevConnectedViaPKCEFlow = SaveGameInstance->bWasConnectedViaPKCEFlow; +} + #if PLATFORM_ANDROID | PLATFORM_IOS | PLATFORM_MAC void UImmutablePassport::OnDeepLinkActivated(FString DeepLink) { @@ -939,17 +974,17 @@ void UImmutablePassport::OnDeepLinkActivated(FString DeepLink) OnHandleDeepLink = nullptr; if (DeepLink.StartsWith(InitData.logoutRedirectUri)) { - // execute on game thread + // execute on game thread to prevent call to Passport instance from another thread if (FTaskGraphInterface::IsRunning()) { FGraphEventRef GameThreadTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this]() { PKCELogoutResponseDelegate.ExecuteIfBound(FImmutablePassportResult{ true, "Logged out" }); PKCELogoutResponseDelegate = nullptr; + ResetStateFlags(IPS_CONNECTED|IPS_PKCE|IPS_IMX); + SavePassportSettings(); }, TStatId(), nullptr, ENamedThreads::GameThread); } - - ResetStateFlags(IPS_CONNECTED|IPS_PKCE|IPS_IMX); } else if (DeepLink.StartsWith(InitData.redirectUri)) { @@ -994,12 +1029,13 @@ void UImmutablePassport::CompleteLoginPKCEFlow(FString Url) PKCEResponseDelegate.ExecuteIfBound(FImmutablePassportResult{false, ErrorMsg}); PKCEResponseDelegate = nullptr; ResetStateFlags(IPS_PKCE|IPS_CONNECTING|IPS_COMPLETING_PKCE); + SavePassportSettings(); } else { FImmutablePassportConnectPKCEData Data = FImmutablePassportConnectPKCEData{Code.GetValue(), State.GetValue()}; - CallJS(IsStateFlagSet(IPS_IMX)? ImmutablePassportAction::CONNECT_PKCE : ImmutablePassportAction::LOGIN_PKCE, + CallJS(IsStateFlagsSet(IPS_IMX)? ImmutablePassportAction::CONNECT_PKCE : ImmutablePassportAction::LOGIN_PKCE, UStructToJsonString(Data), PKCEResponseDelegate, FImtblJSResponseDelegate::CreateUObject(this, &UImmutablePassport::OnConnectPKCEResponse)); } } @@ -1037,7 +1073,7 @@ void UImmutablePassport::HandleOnLoginPKCEDismissed() // all required details (e.g. email address) into Passport // Cannot use IPS_CONNECTING as that is set when PKCE flow is initiated. Here we are checking against the second // half of the PKCE flow. - if (!IsStateFlagSet(IPS_COMPLETING_PKCE)) + if (!IsStateFlagsSet(IPS_COMPLETING_PKCE)) { // User hasn't entered all required details (e.g. email address) into // Passport yet diff --git a/Source/Immutable/Private/Immutable/ImmutableSaveGame.cpp b/Source/Immutable/Private/Immutable/ImmutableSaveGame.cpp new file mode 100644 index 0000000..c2eceb0 --- /dev/null +++ b/Source/Immutable/Private/Immutable/ImmutableSaveGame.cpp @@ -0,0 +1,7 @@ +#include "Immutable/ImmutableSaveGame.h" + + +UImmutableSaveGame::UImmutableSaveGame(const FObjectInitializer& ObjectInitializer) +{ + bWasConnectedViaPKCEFlow = false; +} \ No newline at end of file diff --git a/Source/Immutable/Private/Immutable/Mac/ImmutableMac.cpp b/Source/Immutable/Private/Immutable/Mac/ImmutableMac.cpp index 411f744..8707f50 100644 --- a/Source/Immutable/Private/Immutable/Mac/ImmutableMac.cpp +++ b/Source/Immutable/Private/Immutable/Mac/ImmutableMac.cpp @@ -93,6 +93,7 @@ ASWebAuthenticationSession *_authSession; passport->HandleDeepLink(callbackURL.absoluteString); } } else { + IMTBL_ERR("callbackURL is empty"); return; } }]; diff --git a/Source/Immutable/Public/Immutable/ImmutablePassport.h b/Source/Immutable/Public/Immutable/ImmutablePassport.h index 67c1a0b..9f3605f 100644 --- a/Source/Immutable/Public/Immutable/ImmutablePassport.h +++ b/Source/Immutable/Public/Immutable/ImmutablePassport.h @@ -40,7 +40,7 @@ template TOptional JsonObjectToUStruct(const } /** - * + * Immutable sdk Passport object */ UCLASS() class IMMUTABLE_API UImmutablePassport : public UObject @@ -228,8 +228,13 @@ class IMMUTABLE_API UImmutablePassport : public UObject void SetStateFlags(uint8 StateIn); void ResetStateFlags(uint8 StateIn); - bool IsStateFlagSet(uint8 StateIn) const; + bool IsStateFlagsSet(uint8 StateIn) const; +private: + + void SavePassportSettings(); + void LoadPassportSettings(); + private: enum EImmutablePassportStateFlags : uint8 @@ -244,4 +249,6 @@ class IMMUTABLE_API UImmutablePassport : public UObject }; uint8 StateFlags = IPS_NONE; + bool bIsPrevConnectedViaPKCEFlow = false; + }; diff --git a/Source/Immutable/Public/Immutable/ImmutableSaveGame.h b/Source/Immutable/Public/Immutable/ImmutableSaveGame.h new file mode 100644 index 0000000..a49097a --- /dev/null +++ b/Source/Immutable/Public/Immutable/ImmutableSaveGame.h @@ -0,0 +1,23 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "GameFramework/SaveGame.h" + +#include "ImmutableSaveGame.generated.h" + +/** + * Immutable sdk save game object + */ +UCLASS(BlueprintType, Blueprintable) +class IMMUTABLE_API UImmutableSaveGame : public USaveGame +{ + GENERATED_UCLASS_BODY() + +public: + + /** check if player logged in/connected with PKCE flow previously */ + UPROPERTY(VisibleAnywhere, Category = "Immutable") + bool bWasConnectedViaPKCEFlow; + +}; \ No newline at end of file