Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file modified Content/BlueprintSampleContent/ImtblUnauthenticatedLevel.umap
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,24 @@ void UImtblConnectionAsyncActions::Activate()
{
FString Error = "Connect failed due to missing world or world context object.";
IMTBL_WARN("%s", *Error)
Failed.Broadcast(Error);
Internal_DynamicMulticastDelegate_OnFailed.Broadcast(Error);

return;
}

GetSubsystem()->WhenReady(this, &UImtblConnectionAsyncActions::DoConnect);
}

UImtblConnectionAsyncActions::FPassportConnectOutputPin* UImtblConnectionAsyncActions::DynamicMulticastDelegate_OnSuccess()
{
return &Internal_DynamicMulticastDelegate_OnSuccess;
}

UImtblConnectionAsyncActions::FPassportConnectOutputPin* UImtblConnectionAsyncActions::DynamicMulticastDelegate_OnFailed()
{
return &Internal_DynamicMulticastDelegate_OnFailed;
}

void UImtblConnectionAsyncActions::DoConnect(TWeakObjectPtr<UImtblJSConnector> JSConnector)
{
auto Passport = GetSubsystem()->GetPassport();
Expand All @@ -68,10 +78,10 @@ void UImtblConnectionAsyncActions::OnConnect(FImmutablePassportResult Result)
{
if (Result.Success)
{
Success.Broadcast(TEXT(""));
Internal_DynamicMulticastDelegate_OnSuccess.Broadcast(TEXT(""));
}
else
{
Failed.Broadcast(Result.Error);
Internal_DynamicMulticastDelegate_OnFailed.Broadcast(Result.Error);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include "Immutable/Browser/ImmutableBaseBrowserWidget.h"

#include "Immutable/Misc/ImtblLogging.h"

#define LOCTEXT_NAMESPACE "BaseBrowserWidget"

void UImmutableBaseBrowserWidget::ReleaseSlateResources(bool bReleaseChildren)
{
Super::ReleaseSlateResources(bReleaseChildren);

#if USING_BUNDLED_CEF
WebBrowserWidget.Reset();
#endif
}

bool UImmutableBaseBrowserWidget::IsPageLoaded() const
{
#if USING_BUNDLED_CEF
return WebBrowserWidget.IsValid() && WebBrowserWidget->IsLoaded();
#endif
return false;
}

void UImmutableBaseBrowserWidget::LoadURL(FString NewURL) const
{
#if USING_BUNDLED_CEF
if (WebBrowserWidget.IsValid())
{
return WebBrowserWidget->LoadURL(NewURL);
}
#endif
}

void UImmutableBaseBrowserWidget::LoadString(FString Contents, FString DummyURL)
{
#if USING_BUNDLED_CEF
if (WebBrowserWidget.IsValid())
{
WebBrowserWidget->LoadString(Contents, DummyURL);
}
#endif
}

FImmutableBrowserConsoleMessageDynamicMulticastDelegate* UImmutableBaseBrowserWidget::DynamicMulticastDelegate_OnConsoleMessage()
{
return &Internal_DynamicMulticastDelegate_OnConsoleMessage;
}

FSimpleMulticastDelegate UImmutableBaseBrowserWidget::MulticastDelegate_OnLoadCompleted()
{
return Internal_MulticastDelegate_OnLoadCompleted;
}

FSimpleMulticastDelegate* UImmutableBaseBrowserWidget::MulticastDelegate_OnBrowserCreated()
{
return &Internal_MulticastDelegate_OnBrowserCreated;
}

TSharedRef<SWidget> UImmutableBaseBrowserWidget::RebuildWidget()
{
if (IsDesignTime())
{
// Show placeholder in editor
return SNew(SBox)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("BrowserPlaceholder", "Browser Widget"))
];
}
else
{
#if USING_BUNDLED_CEF
// Create the web browser widget
WebBrowserWidget = SNew(SWebBrowser)
.InitialURL(InitialURL)
.ShowControls(false)
.SupportsTransparency(bSupportsTransparency)
.ShowInitialThrobber(bShowInitialThrobber)
#if PLATFORM_ANDROID || PLATFORM_IOS
.OnLoadCompleted(BIND_UOBJECT_DELEGATE(FSimpleDelegate, HandleOnLoadCompleted))
#endif
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
.OnConsoleMessage(BIND_UOBJECT_DELEGATE(FOnConsoleMessageDelegate, HandleOnConsoleMessage))
#endif
;

return WebBrowserWidget.ToSharedRef();
#else
// Fallback for non-CEF builds
return
SNew(SBox)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(LOCTEXT("NoCEF", "Browser Not Available"))
];
#endif
}
}

void UImmutableBaseBrowserWidget::OnWidgetRebuilt()
{
Super::OnWidgetRebuilt();

OnBrowserCreated();
}

#if PLATFORM_ANDROID || PLATFORM_IOS
void UImmutableBaseBrowserWidget::HandleOnLoadCompleted()
{
// Default mobile load handling
HandleLoadCompleted();
}
#endif

#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1
void UImmutableBaseBrowserWidget::HandleOnConsoleMessage(const FString& Message, const FString& Source, int32 Line, EWebBrowserConsoleLogSeverity Severity)
{
UE_LOG(LogImmutable, Log, TEXT("Browser console message: %s, Source: %s, Line: %d, Severity: %d"), *Message, *Source, Line, Severity);
HandleConsoleMessage(Message, Source, Line, static_cast<int32>(Severity));
}
#endif

void UImmutableBaseBrowserWidget::HandleLoadCompleted()
{
Internal_MulticastDelegate_OnLoadCompleted.Broadcast();
}

void UImmutableBaseBrowserWidget::HandleConsoleMessage(const FString& Message, const FString& Source, int32 Line, int32 Severity)
{
Internal_DynamicMulticastDelegate_OnConsoleMessage.Broadcast(Message, Source, Line, Severity);
}

void UImmutableBaseBrowserWidget::OnBrowserCreated()
{
Internal_MulticastDelegate_OnBrowserCreated.Broadcast();
}

#undef LOCTEXT_NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "Immutable/Browser/ImmutableJSConnectorBrowserWidget.h"

#include "Immutable/ImtblJSConnector.h"

void UImmutableJSConnectorBrowserWidget::PostInitProperties()
{
Super::PostInitProperties();

if (!IsTemplate())
{
JSConnector = NewObject<UImtblJSConnector>(this);
JSConnector->ExecuteJs.BindUObject(this, &ThisClass::ExecuteJavaScript);
}
}

UImtblJSConnector* UImmutableJSConnectorBrowserWidget::GetJSConnector() const
{
return JSConnector;
}

void UImmutableJSConnectorBrowserWidget::ExecuteJavaScript(const FString& ScriptText) const
{
#if USING_BUNDLED_CEF
if (WebBrowserWidget.IsValid())
{
WebBrowserWidget->ExecuteJavascript(ScriptText);
}
#endif
}

bool UImmutableJSConnectorBrowserWidget::BindUObject(const FString& Name, UObject* Object, bool bIsPermanent) const
{
#if USING_BUNDLED_CEF
if (!WebBrowserWidget.IsValid() || !Object)
{
UE_LOG(LogImmutable, Warning, TEXT("Could not bind UObject '%s' to browser, WebBrowserWidget is null"), *Object->GetName());
return false;
}

WebBrowserWidget->BindUObject(Name, Object, bIsPermanent);
return true;
#endif
return false;
}

void UImmutableJSConnectorBrowserWidget::OnBrowserCreated()
{
Super::OnBrowserCreated();

SetupJavaScriptBindings();
}

void UImmutableJSConnectorBrowserWidget::SetupJavaScriptBindings()
{
if (JSConnector && JSConnector->IsBound())
{
return;
}

if (JSConnector)
{
if (BindUObject(UImtblJSConnector::JSObjectName(), JSConnector))
{
JSConnector->Init(IsPageLoaded());
}
}
}
7 changes: 7 additions & 0 deletions Source/Immutable/Private/Immutable/ImmutableDataTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "Immutable/ImmutableDataTypes.h"

#include "Immutable/Actions/ImtblConnectImxAsyncAction.h"

#if PLATFORM_WINDOWS
#include "Immutable/Windows/ImmutablePKCEWindows.h"
#endif
Expand Down Expand Up @@ -115,4 +117,9 @@ TSharedPtr<FJsonObject> FImmutableDirectLoginOptions::ToJsonObject() const
JsonObject->SetStringField(TEXT("marketingConsentStatus"), StaticEnum<EImmutableMarketingConsentStatus>()->GetNameStringByValue(static_cast<int64>(MarketingConsentStatus)).ToLower());

return JsonObject;
}

bool UImmutableDirectLoginOptionsStatics::FromJSResponse(const FImtblJSResponse& Response, FImmutableDirectLoginOptions& DirectLoginOptions)
{
return FJsonObjectConverter::JsonObjectToUStruct(Response.JsonObject.ToSharedRef(), &DirectLoginOptions);
}
3 changes: 2 additions & 1 deletion Source/Immutable/Private/Immutable/ImmutablePassport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ void UImmutablePassport::Connect(bool IsConnectImx, const FImtblPassportResponse
#if PLATFORM_WINDOWS
// Verify PKCEData is null before initializing to ensure we're not overriding an active PKCE operation.
// A non-null value indicates another PKCE operation is already in progress.
ensureAlways(!PKCEData);
PKCEData = UImmutablePKCEWindows::Initialise(InitData);
if (PKCEData)
{
Expand All @@ -118,6 +117,8 @@ void UImmutablePassport::Connect(bool IsConnectImx, const FImtblPassportResponse
RequestObject->SetObjectField(TEXT("directLoginOptions"), DirectLoginOptionsObject);
}

RequestObject->SetStringField(TEXT("imPassportTraceId"), DirectLoginOptions.ImPassportTraceId);

// Convert to JSON string
FString PKCERequestJson;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&PKCERequestJson);
Expand Down
52 changes: 39 additions & 13 deletions Source/Immutable/Private/Immutable/ImtblBrowserUserWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
#include "Blueprint/WidgetTree.h"
#include "Components/CanvasPanel.h"
#include "Components/CanvasPanelSlot.h"
#include "Components/PanelWidget.h"
#include "Components/ScaleBox.h"
#include "Components/ScaleBoxSlot.h"

#include "Immutable/Browser/ImmutableJSConnectorBrowserWidget.h"
#include "Immutable/ImmutableUtilities.h"
#include "Immutable/Misc/ImtblLogging.h"
#include "ImtblBrowserWidget.h"
#include "Immutable/ImtblJSConnector.h"

TSharedRef<SWidget> UImtblBrowserUserWidget::RebuildWidget()
{
Expand All @@ -35,21 +35,47 @@ TSharedRef<SWidget> UImtblBrowserUserWidget::RebuildWidget()
RootWidget->AddChild(ScaleBox);
if (ScaleBox)
{
Browser = WidgetTree->ConstructWidget<UImtblBrowserWidget>(UImtblBrowserWidget::StaticClass(), TEXT("ImmutableBrowserWidget"));
ScaleBox->AddChild(Browser);
W_Browser = WidgetTree->ConstructWidget<UImmutableJSConnectorBrowserWidget>(UImmutableJSConnectorBrowserWidget::StaticClass(), TEXT("GameBridgeWidget"));
W_Browser->MulticastDelegate_OnLoadCompleted().AddWeakLambda(this, []()
{
#if PLATFORM_ANDROID | PLATFORM_IOS
FString IndexURL = "file:///immutable/index.html";

#if USING_BUNDLED_CEF
if (WebBrowserWidget->GetUrl() == IndexURL)
{
JSConnector->SetMobileBridgeReady();
}
else
{
UE_LOG(LogImmutable, Error, TEXT("Immutable Browser Widget Url don't match: (loaded : %s, required: %s)"), *WebBrowserWidget->GetUrl(), *IndexURL);
}
#endif
#endif
});
W_Browser->MulticastDelegate_OnBrowserCreated()->AddWeakLambda(this, [this]()
{
FString JavaScript;
if (FImmutableUtilities::LoadGameBridge(JavaScript))
{
FString IndexHtml = FString("<!doctype html><html lang='en'><head><meta " "charset='utf-8'><title>GameSDK Bridge</title><script>") + JavaScript + FString("</script></head><body><h1>Bridge Running</h1></body></html>");
W_Browser->LoadString(IndexHtml, TEXT("file:///immutable/index.html"));
}
});
ScaleBox->AddChild(W_Browser);
if (UCanvasPanelSlot* RootWidgetSlot = Cast<UCanvasPanelSlot>(ScaleBox->Slot))
{
#if PLATFORM_ANDROID | PLATFORM_IOS
// Android webview needs to be at least 1px to 1px big to work
// but it can be off screen
RootWidgetSlot->SetAnchors(FAnchors(0, 0, 0, 0));
RootWidgetSlot->SetOffsets(FMargin(-1, -1, 1, 1));
// Android webview needs to be at least 1px to 1px big to work
// but it can be off screen
RootWidgetSlot->SetAnchors(FAnchors(0, 0, 0, 0));
RootWidgetSlot->SetOffsets(FMargin(-1, -1, 1, 1));
#else
RootWidgetSlot->SetAnchors(FAnchors(0, 0, 1, 1));
RootWidgetSlot->SetOffsets(DefaultOffsets);
#endif
}
if (UScaleBoxSlot* ScaleBoxSlot = Cast<UScaleBoxSlot>(Browser->Slot))
if (UScaleBoxSlot* ScaleBoxSlot = Cast<UScaleBoxSlot>(W_Browser->Slot))
{
ScaleBoxSlot->SetHorizontalAlignment(HAlign_Fill);
ScaleBoxSlot->SetVerticalAlignment(VAlign_Fill);
Expand Down Expand Up @@ -87,11 +113,11 @@ void UImtblBrowserUserWidget::OnWidgetRebuilt()

TWeakObjectPtr<class UImtblJSConnector> UImtblBrowserUserWidget::GetJSConnector() const
{
if (!Browser)
if (!W_Browser)
{
IMTBL_WARN("JSConnector requested before Browser was initialized");
return nullptr;
}

return Browser->GetJSConnector();
}
return W_Browser->GetJSConnector();
}
Loading
Loading