diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..da77e55 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,78 @@ +name: Build Plugin + +on: + workflow_dispatch: + push: + branches: [ main, ci-cd ] + paths: + - 'Config/**' + - 'Content/**' + - 'Resources/**' + - 'Source/**' + - 'Thirdweb.uplugin' + - '.github/workflows/build.yml' + pull_request: + branches: [ main, ci-cd ] + paths: + - 'Config/**' + - 'Content/**' + - 'Resources/**' + - 'Source/**' + - 'Thirdweb.uplugin' + - '.github/workflows/build.yml' + +jobs: + build: + name: Build Target + strategy: + fail-fast: true + matrix: + engine: [ "5.4", "5.3", "5.2" ] # ["5.5", "5.1", "5.0"] + os: [ ubuntu-latest-8, ubuntu-24.04-arm64 ] # windows-11-desktop, macos-13 + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Set Linux Target + if: ${{ matrix.os == 'ubuntu-latest-8' || matrix.os == 'ubuntu-24.04-arm64' }} + run: | + export LINUX_TARGET_TYPE="Linux" + if [[ "${{ matrix.os }}" == "ubuntu-24.04-arm64" ]]; then + export LINUX_TARGET_TYPE="LinuxArm64" + fi + echo "LINUX_TARGET_TYPE=$LINUX_TARGET_TYPE" >> "$GITHUB_ENV" + + - name: Build Plugin (Linux) + if: ${{ matrix.os == 'ubuntu-latest-8' || matrix.os == 'ubuntu-24.04-arm64' }} + uses: addnab/docker-run-action@v3 + with: + username: ${{ secrets.GHCR_USERNAME }} + password: ${{ secrets.GHCR_TOKEN }} + registry: ghcr.io + image: ghcr.io/epicgames/unreal-engine:dev-slim-${{ matrix.engine }} + options: -v ${{ github.workspace }}:/plugin + run: | + Engine/Build/BatchFiles/RunUAT.sh \ + BuildPlugin \ + -NoHostPlatform \ + -TargetPlatform=Linux${{ env.LINUX_TARGET_TYPE }} \ + -Package=/home/ue4//build \ + -Plugin="/plugin/Thirdweb.uplugin" + + # - name: Downgrade Xcode + # if: ${{ matrix.os == 'macos-13' }} + # uses: maxim-lobanov/setup-xcode@v1 + # with: + # xcode-version: '14.1.0' + + # - name: Debug Clang + # if: ${{ matrix.platform == 'ios' }} + # run: clang --version + + # - name: Archive Build Artifact + # uses: actions/upload-artifact@v4 + # with: + # name: ${{ format('{0}-{1}-{2}', matrix.platform, matrix.arch, github.run_id) }} + # path: | + # target/*/release/libthirdweb.a + # target/*/release/thirdweb.lib diff --git a/Content/Defaults/WBP_Thirdweb_OAuthOverlay.uasset b/Content/Defaults/WBP_Thirdweb_OAuthOverlay.uasset index b0efd51..511e752 100644 Binary files a/Content/Defaults/WBP_Thirdweb_OAuthOverlay.uasset and b/Content/Defaults/WBP_Thirdweb_OAuthOverlay.uasset differ diff --git a/Content/Examples/NodeDocs/Thirdweb_AllNodes.uasset b/Content/Examples/NodeDocs/Thirdweb_AllNodes.uasset index 661f474..5f7bd1d 100644 Binary files a/Content/Examples/NodeDocs/Thirdweb_AllNodes.uasset and b/Content/Examples/NodeDocs/Thirdweb_AllNodes.uasset differ diff --git a/Content/Examples/Widgets/WBP_Thirdweb_Smart.uasset b/Content/Examples/Widgets/WBP_Thirdweb_Smart.uasset index 1358ea2..53e59b3 100644 Binary files a/Content/Examples/Widgets/WBP_Thirdweb_Smart.uasset and b/Content/Examples/Widgets/WBP_Thirdweb_Smart.uasset differ diff --git a/README.md b/README.md index 83e8545..9194567 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Legend: ✅ Supported | ❌ Unsupported | ➖ Not applicable ## Documentation -Documentation is available at https://portal.thirdweb.com/unreal +Documentation is available at https://portal.thirdweb.com/unreal-engine [^1]: Unreal Engine's Marketplace Guidelines - Section 3.1.b https://www.unrealengine.com/en-US/marketplace-guidelines#31b diff --git a/Source/Thirdweb/Private/AsyncTasks/Engine/Contract/AsyncTaskContractWriteContract.cpp b/Source/Thirdweb/Private/AsyncTasks/Engine/Contract/AsyncTaskContractWriteContract.cpp index 8a0b285..5a38db0 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Engine/Contract/AsyncTaskContractWriteContract.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Engine/Contract/AsyncTaskContractWriteContract.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Engine/Contract/AsyncTaskContractWriteContract.h" +#include "ThirdwebRuntimeSettings.h" #include "Components/SlateWrapperTypes.h" #include "Engine/ThirdwebEngine.h" diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebGetLinkedAccounts.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebGetLinkedAccounts.cpp index 586ddcc..a142a72 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebGetLinkedAccounts.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebGetLinkedAccounts.cpp @@ -3,7 +3,6 @@ #include "AsyncTasks/Wallets/InApp/AsyncTaskThirdwebGetLinkedAccounts.h" #include "Async/TaskGraphInterfaces.h" - #include "Components/SlateWrapperTypes.h" void UAsyncTaskThirdwebGetLinkedAccounts::Activate() diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebInAppSignMessage.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebInAppSignMessage.cpp index 3f1e517..871eccc 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebInAppSignMessage.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebInAppSignMessage.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/InApp/AsyncTaskThirdwebInAppSignMessage.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" void UAsyncTaskThirdwebInAppSignMessage::Activate() diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLink.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLink.cpp index 028d157..7b020bc 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLink.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLink.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLink.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" UAsyncTaskThirdwebLink* UAsyncTaskThirdwebLink::Link(UObject* WorldContextObject, const FInAppWalletHandle& Wallet, const FInAppWalletHandle& NewWallet, const FString& Input, const FString& Signature) diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLoginWithOAuth.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLoginWithOAuth.cpp index ce2da6b..7dcafbc 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLoginWithOAuth.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebLoginWithOAuth.cpp @@ -4,15 +4,11 @@ #include "ThirdwebLog.h" #include "TimerManager.h" - +#include "Async/TaskGraphInterfaces.h" #include "Blueprint/UserWidget.h" - #include "Browser/ThirdwebOAuthBrowserUserWidget.h" - #include "Engine/World.h" - #include "Kismet/GameplayStatics.h" - #include "Misc/DateTime.h" void UAsyncTaskThirdwebLoginWithOAuth::Activate() diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSendOTP.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSendOTP.cpp index a9d5d35..df21f27 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSendOTP.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSendOTP.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSendOTP.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" void UAsyncTaskThirdwebSendOTP::Activate() diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSignIn.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSignIn.cpp index 8b00fb6..aed1c98 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSignIn.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSignIn.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/InApp/AsyncTaskThirdwebSignIn.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" UAsyncTaskThirdwebSignIn* UAsyncTaskThirdwebSignIn::SignIn(UObject* WorldContextObject, const FInAppWalletHandle& Wallet, const FString& Input, const FString& Signature) diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebCreateSmartWallet.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebCreateSmartWallet.cpp index a472ff6..23d7558 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebCreateSmartWallet.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebCreateSmartWallet.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebCreateSmartWallet.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" #include "Wallets/ThirdwebSmartWalletHandle.h" diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebInAppCreateWalletBase.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebInAppCreateWalletBase.cpp index fc5efb3..c7f17a8 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebInAppCreateWalletBase.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebInAppCreateWalletBase.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/InApp/Create/AsyncTaskThirdwebInAppCreateWalletBase.h" +#include "Async/TaskGraphInterfaces.h" #include "Wallets/ThirdwebInAppWalletHandle.h" void UAsyncTaskThirdwebInAppCreateWalletBase::HandleResponse(const FInAppWalletHandle& Wallet) diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebAddAdmin.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebAddAdmin.cpp index ecaa82b..95d3f67 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebAddAdmin.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebAddAdmin.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/Smart/AsyncTaskThirdwebAddAdmin.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" void UAsyncTaskThirdwebAddAdmin::Activate() diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebCreateSessionKey.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebCreateSessionKey.cpp index 600b20c..6509d08 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebCreateSessionKey.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebCreateSessionKey.cpp @@ -17,12 +17,14 @@ void UAsyncTaskThirdwebCreateSessionKey::Activate() ); } -UAsyncTaskThirdwebCreateSessionKey* UAsyncTaskThirdwebCreateSessionKey::CreateSessionKey(UObject* WorldContextObject, - const FSmartWalletHandle& Wallet, - const FString& Signer, - const TArray& ApprovedTargets, - const FString& NativeTokenLimitPerTransactionInWei, - const FDateTime& PermissionEnd) +UAsyncTaskThirdwebCreateSessionKey* UAsyncTaskThirdwebCreateSessionKey::CreateSessionKey( + UObject* WorldContextObject, + const FSmartWalletHandle& Wallet, + const FString& Signer, + const TArray& ApprovedTargets, + const FString& NativeTokenLimitPerTransactionInWei, + const FDateTime& PermissionEnd +) { if (!WorldContextObject) { @@ -54,7 +56,7 @@ void UAsyncTaskThirdwebCreateSessionKey::HandleResponse(const FString& TxHash) }, TStatId(), nullptr, ENamedThreads::GameThread); return; } - + Success.Broadcast(TxHash, TEXT("")); SetReadyToDestroy(); } diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetActiveSigners.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetActiveSigners.cpp index 9a9d474..9854858 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetActiveSigners.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetActiveSigners.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetActiveSigners.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" #include "Containers/ThirdwebSigner.h" diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetAdmins.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetAdmins.cpp index b58f559..79d72c4 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetAdmins.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetAdmins.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/Smart/AsyncTaskThirdwebGetAdmins.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" void UAsyncTaskThirdwebGetAdmins::Activate() diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsActiveSigner.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsActiveSigner.cpp index a51772e..50bd70b 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsActiveSigner.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsActiveSigner.cpp @@ -2,9 +2,9 @@ #include "AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsActiveSigner.h" -#include "Containers/ThirdwebSigner.h" - +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" +#include "Containers/ThirdwebSigner.h" void UAsyncTaskThirdwebIsActiveSigner::Activate() { diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsDeployed.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsDeployed.cpp index 531e4db..faa019e 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsDeployed.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsDeployed.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/Smart/AsyncTaskThirdwebIsDeployed.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" void UAsyncTaskThirdwebIsDeployed::Activate() diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebRevokeSessionKey.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebRevokeSessionKey.cpp index 1b9fd65..55b4995 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebRevokeSessionKey.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebRevokeSessionKey.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/Smart/AsyncTaskThirdwebRevokeSessionKey.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" void UAsyncTaskThirdwebRevokeSessionKey::Activate() diff --git a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebSmartSignMessage.cpp b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebSmartSignMessage.cpp index d3d7200..db42640 100644 --- a/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebSmartSignMessage.cpp +++ b/Source/Thirdweb/Private/AsyncTasks/Wallets/Smart/AsyncTaskThirdwebSmartSignMessage.cpp @@ -2,6 +2,7 @@ #include "AsyncTasks/Wallets/Smart/AsyncTaskThirdwebSmartSignMessage.h" +#include "Async/TaskGraphInterfaces.h" #include "Components/SlateWrapperTypes.h" void UAsyncTaskThirdwebSmartSignMessage::Activate() diff --git a/Source/Thirdweb/Private/Browser/ThirdwebOAuthBrowserWidget.cpp b/Source/Thirdweb/Private/Browser/ThirdwebOAuthBrowserWidget.cpp index 483bc1a..2111cc8 100644 --- a/Source/Thirdweb/Private/Browser/ThirdwebOAuthBrowserWidget.cpp +++ b/Source/Thirdweb/Private/Browser/ThirdwebOAuthBrowserWidget.cpp @@ -4,11 +4,9 @@ #include "ThirdwebLog.h" #include "ThirdwebRuntimeSettings.h" - #include "Async/Async.h" - +#include "Async/TaskGraphInterfaces.h" #include "GenericPlatform/GenericPlatformHttp.h" - #include "Widgets/Layout/SBox.h" #include "Widgets/Text/STextBlock.h" #if WITH_CEF diff --git a/Source/Thirdweb/Private/Containers/ThirdwebLinkedAccount.cpp b/Source/Thirdweb/Private/Containers/ThirdwebLinkedAccount.cpp index dda6514..c0cc900 100644 --- a/Source/Thirdweb/Private/Containers/ThirdwebLinkedAccount.cpp +++ b/Source/Thirdweb/Private/Containers/ThirdwebLinkedAccount.cpp @@ -2,6 +2,8 @@ #include "Containers/ThirdwebLinkedAccount.h" +#include "Dom/JsonObject.h" + FThirdwebLinkedAccount FThirdwebLinkedAccount::FromJson(const TSharedPtr& JsonObject) { FThirdwebLinkedAccount LinkedAccount; diff --git a/Source/Thirdweb/Private/Containers/ThirdwebMultipartFormData.cpp b/Source/Thirdweb/Private/Containers/ThirdwebMultipartFormData.cpp index 4da29ed..de85916 100644 --- a/Source/Thirdweb/Private/Containers/ThirdwebMultipartFormData.cpp +++ b/Source/Thirdweb/Private/Containers/ThirdwebMultipartFormData.cpp @@ -2,6 +2,7 @@ #include "Containers/ThirdwebMultipartFormData.h" +#include "Containers/StringConv.h" #include "Misc/FileHelper.h" #include "Misc/Guid.h" diff --git a/Source/Thirdweb/Private/Internal/ThirdwebHeaders.cpp b/Source/Thirdweb/Private/Internal/ThirdwebHeaders.cpp index a4d4629..a4f7a88 100644 --- a/Source/Thirdweb/Private/Internal/ThirdwebHeaders.cpp +++ b/Source/Thirdweb/Private/Internal/ThirdwebHeaders.cpp @@ -2,6 +2,7 @@ #include "Internal/ThirdwebHeaders.h" +#include "Containers/UnrealString.h" #include "Interfaces/IHttpRequest.h" FThirdwebHeaders::FThirdwebHeaders() @@ -16,6 +17,15 @@ void FThirdwebHeaders::Set(const FString& Name, const FString& Value, const bool } } +void FThirdwebHeaders::SetMany(TArray> Pairs) +{ + for (const auto& Pair : Pairs) + { + Set(Pair.Key, Pair.Value); + } +} + + bool FThirdwebHeaders::Remove(const FString& Name) { return Headers.Remove(Name) > 0; diff --git a/Source/Thirdweb/Private/ThirdwebUtils.cpp b/Source/Thirdweb/Private/ThirdwebUtils.cpp index 9b94136..6954953 100644 --- a/Source/Thirdweb/Private/ThirdwebUtils.cpp +++ b/Source/Thirdweb/Private/ThirdwebUtils.cpp @@ -11,6 +11,7 @@ #include "Thirdweb.h" #include "ThirdwebLog.h" #include "ThirdwebRuntimeSettings.h" +#include "Async/TaskGraphInterfaces.h" #include "Containers/ThirdwebIPFSUploadResult.h" #include "Containers/ThirdwebMultipartFormData.h" #include "Dom/JsonObject.h" @@ -21,10 +22,12 @@ #include "Interfaces/IHttpRequest.h" #include "Interfaces/IHttpResponse.h" #include "Interfaces/IPluginManager.h" +#include "Internal/ThirdwebHeaders.h" #include "Kismet/GameplayStatics.h" #include "Kismet/KismetStringLibrary.h" #include "Misc/Base64.h" #include "Misc/DefaultValueHelper.h" +#include "Modules/ModuleManager.h" #include "Policies/CondensedJsonPrintPolicy.h" #include "Serialization/JsonReader.h" #include "Serialization/JsonSerializer.h" @@ -419,8 +422,7 @@ namespace ThirdwebUtils { if (!IsInGameThread()) { - // Retry on the GameThread. - const FWalletHandle WalletCopy; + const FWalletHandle WalletCopy = Wallet; FFunctionGraphTask::CreateAndDispatchWhenReady([WalletCopy]() { SendConnectEvent(WalletCopy); @@ -436,13 +438,17 @@ namespace ThirdwebUtils const TSharedRef Request = HttpModule.CreateRequest(); Request->SetVerb("POST"); Request->SetURL("https://c.thirdweb.com/event"); - Request->SetHeader("Content-Type", "application/json"); - Request->SetHeader("x-sdk-name", "UnrealEngineSDK"); - Request->SetHeader("x-sdk-os", UGameplayStatics::GetPlatformName()); - Request->SetHeader("x-sdk-platform", "unreal-engine"); - Request->SetHeader("x-sdk-version", GetPluginVersion()); - Request->SetHeader("x-client-id", Settings->GetClientId()); - Request->SetHeader("x-bundle-id", Settings->GetBundleId()); + FThirdwebHeaders Headers; + Headers.SetMany({ + {TEXT("Content-Type"), TEXT("application/json")}, + {TEXT("x-sdk-name"), TEXT("UnrealEngineSDK")}, + {TEXT("x-sdk-os"), UGameplayStatics::GetPlatformName()}, + {TEXT("x-sdk-platform"), TEXT("unreal-engine")}, + {TEXT("x-sdk-version"), GetPluginVersion()}, + {TEXT("x-client-id"), Settings->GetClientId()}, + {TEXT("x-bundle-id"), Settings->GetBundleId()}, + }); + Headers.UpdateRequest(Request); Request->SetTimeout(5.0f); // ReSharper disable once CppLocalVariableMayBeConst @@ -451,7 +457,7 @@ namespace ThirdwebUtils JsonObject->SetStringField(TEXT("action"), TEXT("connect")); JsonObject->SetStringField(TEXT("walletAddress"), Wallet.ToAddress()); JsonObject->SetStringField(TEXT("walletType"), Wallet.GetTypeString()); - Request->SetContentAsString(ThirdwebUtils::Json::ToString(JsonObject)); + Request->SetContentAsString(Json::ToString(JsonObject)); Request->ProcessRequest(); } diff --git a/Source/Thirdweb/Private/Wallets/ThirdwebSmartWalletHandle.cpp b/Source/Thirdweb/Private/Wallets/ThirdwebSmartWalletHandle.cpp index 2a533e1..131e20a 100644 --- a/Source/Thirdweb/Private/Wallets/ThirdwebSmartWalletHandle.cpp +++ b/Source/Thirdweb/Private/Wallets/ThirdwebSmartWalletHandle.cpp @@ -43,7 +43,7 @@ void FSmartWalletHandle::Create(const FInAppWalletHandle& InInAppWallet, const FStringDelegate& ErrorDelegate) { CHECK_DELEGATES(SuccessDelegate, ErrorDelegate); - + if (!InInAppWallet.IsValid()) { if (ErrorDelegate.IsBound()) @@ -52,7 +52,7 @@ void FSmartWalletHandle::Create(const FInAppWalletHandle& InInAppWallet, } return; } - + UE::Tasks::Launch(UE_SOURCE_LOCATION, [InInAppWallet, ChainID, bGasless, Factory, AccountOverride, SuccessDelegate, ErrorDelegate] { if (FString Error; Thirdweb::create_smart_wallet( @@ -84,7 +84,7 @@ void FSmartWalletHandle::IsDeployed(const FBoolDelegate& SuccessDelegate, const { CHECK_DELEGATES(SuccessDelegate, ErrorDelegate); CHECK_VALIDITY(ErrorDelegate); - + FSmartWalletHandle ThisCopy = *this; UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, SuccessDelegate, ErrorDelegate] { @@ -99,16 +99,18 @@ void FSmartWalletHandle::IsDeployed(const FBoolDelegate& SuccessDelegate, const }); } -void FSmartWalletHandle::CreateSessionKey(const FString& Signer, - const TArray& ApprovedTargets, - const FString& NativeTokenLimitPerTransactionInWei, - const FDateTime& PermissionEnd, - const FStringDelegate& SuccessDelegate, - const FStringDelegate& ErrorDelegate) +void FSmartWalletHandle::CreateSessionKey( + const FString& Signer, + const TArray& ApprovedTargets, + const FString& NativeTokenLimitPerTransactionInWei, + const FDateTime& PermissionEnd, + const FStringDelegate& SuccessDelegate, + const FStringDelegate& ErrorDelegate +) { CHECK_DELEGATES(SuccessDelegate, ErrorDelegate); CHECK_VALIDITY(ErrorDelegate); - + FDateTime TenYearsFromNow = FDateTime::UtcNow() + FTimespan::FromDays(10 * 365); FDateTime EndTime = TenYearsFromNow; TArray ApprovedTargetsCArray; @@ -167,7 +169,7 @@ void FSmartWalletHandle::RevokeSessionKey(const FString& Signer, const FSimpleDe { CHECK_DELEGATES(SuccessDelegate, ErrorDelegate); CHECK_VALIDITY(ErrorDelegate); - + FSmartWalletHandle ThisCopy = *this; UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, Signer, SuccessDelegate, ErrorDelegate] { @@ -186,7 +188,7 @@ void FSmartWalletHandle::GetAdmins(const FStringArrayDelegate& SuccessDelegate, { CHECK_DELEGATES(SuccessDelegate, ErrorDelegate); CHECK_VALIDITY(ErrorDelegate); - + FSmartWalletHandle ThisCopy = *this; UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, SuccessDelegate, ErrorDelegate] { @@ -219,14 +221,15 @@ void FSmartWalletHandle::AddAdmin(const FString& Signer, const FSimpleDelegate& { CHECK_DELEGATES(SuccessDelegate, ErrorDelegate); CHECK_VALIDITY(ErrorDelegate); - + FSmartWalletHandle ThisCopy = *this; UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, Signer, SuccessDelegate, ErrorDelegate] { if (FString Error; Thirdweb::smart_wallet_add_admin(ThisCopy.GetID(), TO_RUST_STRING(Signer)).AssignResult(Error)) { SuccessDelegate.Execute(); - } else + } + else { ErrorDelegate.Execute(Error); } @@ -237,14 +240,15 @@ void FSmartWalletHandle::RemoveAdmin(const FString& Signer, const FSimpleDelegat { CHECK_DELEGATES(SuccessDelegate, ErrorDelegate); CHECK_VALIDITY(ErrorDelegate); - + FSmartWalletHandle ThisCopy = *this; UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, Signer, SuccessDelegate, ErrorDelegate] { if (FString Error; Thirdweb::smart_wallet_remove_admin(ThisCopy.GetID(), TO_RUST_STRING(Signer)).AssignResult(Error)) { SuccessDelegate.Execute(); - } else + } + else { ErrorDelegate.Execute(Error); } @@ -255,7 +259,7 @@ void FSmartWalletHandle::GetActiveSigners(const FGetActiveSignersDelegate& Succe { CHECK_DELEGATES(SuccessDelegate, ErrorDelegate); CHECK_VALIDITY(ErrorDelegate); - + FSmartWalletHandle ThisCopy = *this; UE::Tasks::Launch(UE_SOURCE_LOCATION, [ThisCopy, SuccessDelegate, ErrorDelegate] { diff --git a/Source/Thirdweb/Public/AsyncTasks/Engine/Contract/AsyncTaskContractWriteContract.h b/Source/Thirdweb/Public/AsyncTasks/Engine/Contract/AsyncTaskContractWriteContract.h index 50bf0ff..891e8bd 100644 --- a/Source/Thirdweb/Public/AsyncTasks/Engine/Contract/AsyncTaskContractWriteContract.h +++ b/Source/Thirdweb/Public/AsyncTasks/Engine/Contract/AsyncTaskContractWriteContract.h @@ -7,26 +7,29 @@ #include "Wallets/ThirdwebSmartWalletHandle.h" #include "AsyncTaskContractWriteContract.generated.h" - UCLASS(Blueprintable, BlueprintType) class THIRDWEB_API UAsyncTaskContractWriteContract : public UAsyncTaskThirdwebBase { GENERATED_BODY() public: - UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly="true", WorldContext="WorldContextObject", AdvancedDisplay="IdempotencyKey,FactoryAddress,TxOverrides,Abi,bSimulateTx", AutoCreateRefTerm="Args,SmartWallet,TxOverrides"), Category="Thirdweb|Engine|Contract") - static UAsyncTaskContractWriteContract* WriteContract(UObject* WorldContextObject, - const int64 ChainId, - const FString& ContractAddress, - const FString& BackendWalletAddress, - const FSmartWalletHandle& SmartWallet, - const FString& FactoryAddress, - const FString& IdempotencyKey, - const FString& FunctionName, - const TArray& Args, - const FThirdwebEngineTransactionOverrides& TxOverrides, - const FString& Abi, - const bool bSimulateTx); + UFUNCTION(BlueprintCallable, + meta=(BlueprintInternalUseOnly="true", WorldContext="WorldContextObject", AdvancedDisplay="IdempotencyKey,FactoryAddress,TxOverrides,Abi,bSimulateTx", + AutoCreateRefTerm="Args,SmartWallet,TxOverrides"), Category="Thirdweb|Engine|Contract") + static UAsyncTaskContractWriteContract* WriteContract( + UObject* WorldContextObject, + const int64 ChainId, + const FString& ContractAddress, + const FString& BackendWalletAddress, + const FSmartWalletHandle& SmartWallet, + const FString& FactoryAddress, + const FString& IdempotencyKey, + const FString& FunctionName, + const TArray& Args, + const FThirdwebEngineTransactionOverrides& TxOverrides, + const FString& Abi, + const bool bSimulateTx + ); virtual void Activate() override; @@ -38,6 +41,8 @@ class THIRDWEB_API UAsyncTaskContractWriteContract : public UAsyncTaskThirdwebBa UPROPERTY(BlueprintAssignable) FWriteContractDelegate Failed; + + protected: UPROPERTY(Transient) int64 ChainId; diff --git a/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/EnglishAuctions/AsyncTaskEnglishAuctionsGet.h b/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/EnglishAuctions/AsyncTaskEnglishAuctionsGet.h index 5bc99b2..b802800 100644 --- a/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/EnglishAuctions/AsyncTaskEnglishAuctionsGet.h +++ b/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/EnglishAuctions/AsyncTaskEnglishAuctionsGet.h @@ -3,10 +3,10 @@ #pragma once #include "AsyncTasks/Engine/Marketplace/AsyncTaskMarketplaceGetBase.h" +#include "Engine/Marketplace/EnglishAuctions/ThirdwebMarketplaceEnglishAuction.h" #include "AsyncTaskEnglishAuctionsGet.generated.h" class UThirdwebMarketplace; -struct FThirdwebMarketplaceEnglishAuction; /** * */ diff --git a/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/EnglishAuctions/AsyncTaskEnglishAuctionsGetWinningBid.h b/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/EnglishAuctions/AsyncTaskEnglishAuctionsGetWinningBid.h index 71962cb..d12a466 100644 --- a/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/EnglishAuctions/AsyncTaskEnglishAuctionsGetWinningBid.h +++ b/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/EnglishAuctions/AsyncTaskEnglishAuctionsGetWinningBid.h @@ -3,9 +3,9 @@ #pragma once #include "AsyncTasks/Engine/Marketplace/AsyncTaskMarketplaceGetBase.h" +#include "Engine/Marketplace/EnglishAuctions/ThirdwebMarketplaceBid.h" #include "AsyncTaskEnglishAuctionsGetWinningBid.generated.h" -struct FThirdwebMarketplaceBid; class UThirdwebMarketplace; /** * @@ -33,7 +33,7 @@ class THIRDWEB_API UAsyncTaskEnglishAuctionsGetWinningBid : public UAsyncTaskMar UPARAM(DisplayName="Listing ID") const FString& ListingId ); - DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGetWinningBidDelegate, const FThirdwebMarketplaceBid&, WinningBid, const FString&, Error); + DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FGetWinningBidDelegate, const FThirdwebMarketplaceBid&, WinningBid, const FString&, Error); UPROPERTY(BlueprintAssignable) FGetWinningBidDelegate Success; diff --git a/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/Offers/AsyncTaskOffersGet.h b/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/Offers/AsyncTaskOffersGet.h index 6b825f8..8e10f43 100644 --- a/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/Offers/AsyncTaskOffersGet.h +++ b/Source/Thirdweb/Public/AsyncTasks/Engine/Marketplace/Offers/AsyncTaskOffersGet.h @@ -3,9 +3,9 @@ #pragma once #include "AsyncTasks/Engine/Marketplace/AsyncTaskMarketplaceGetBase.h" +#include "Engine/Marketplace/Offers/ThirdwebMarketplaceOffer.h" #include "AsyncTaskOffersGet.generated.h" -struct FThirdwebMarketplaceOffer; class UThirdwebMarketplace; /** diff --git a/Source/Thirdweb/Public/AsyncTasks/Engine/Transaction/AsyncTaskTransactionGetTransactionReceipt.h b/Source/Thirdweb/Public/AsyncTasks/Engine/Transaction/AsyncTaskTransactionGetTransactionReceipt.h index d603563..99f95a0 100644 --- a/Source/Thirdweb/Public/AsyncTasks/Engine/Transaction/AsyncTaskTransactionGetTransactionReceipt.h +++ b/Source/Thirdweb/Public/AsyncTasks/Engine/Transaction/AsyncTaskTransactionGetTransactionReceipt.h @@ -3,9 +3,9 @@ #pragma once #include "AsyncTasks/AsyncTaskThirdwebBase.h" +#include "Engine/Transaction/ThirdwebEngineTransactionReceipt.h" #include "AsyncTaskTransactionGetTransactionReceipt.generated.h" -struct FThirdwebEngineTransactionReceipt; UCLASS(Blueprintable, BlueprintType) class THIRDWEB_API UAsyncTaskTransactionGetTransactionReceipt : public UAsyncTaskThirdwebBase diff --git a/Source/Thirdweb/Public/AsyncTasks/Engine/Transaction/AsyncTaskTransactionGetTransactionStatus.h b/Source/Thirdweb/Public/AsyncTasks/Engine/Transaction/AsyncTaskTransactionGetTransactionStatus.h index ddd6cec..c3f496e 100644 --- a/Source/Thirdweb/Public/AsyncTasks/Engine/Transaction/AsyncTaskTransactionGetTransactionStatus.h +++ b/Source/Thirdweb/Public/AsyncTasks/Engine/Transaction/AsyncTaskTransactionGetTransactionStatus.h @@ -3,10 +3,9 @@ #pragma once #include "AsyncTasks/AsyncTaskThirdwebBase.h" +#include "Engine/Transaction/ThirdwebEngineTransactionStatusResult.h" #include "AsyncTaskTransactionGetTransactionStatus.generated.h" -struct FThirdwebEngineTransactionStatusResult; - UCLASS(Blueprintable, BlueprintType) class THIRDWEB_API UAsyncTaskTransactionGetTransactionStatus : public UAsyncTaskThirdwebBase { diff --git a/Source/Thirdweb/Public/Containers/ThirdwebMultipartFormData.h b/Source/Thirdweb/Public/Containers/ThirdwebMultipartFormData.h index 18432fb..7912f23 100644 --- a/Source/Thirdweb/Public/Containers/ThirdwebMultipartFormData.h +++ b/Source/Thirdweb/Public/Containers/ThirdwebMultipartFormData.h @@ -2,6 +2,10 @@ #pragma once +#include "Containers/Array.h" +#include "Containers/UnrealString.h" +#include "HAL/Platform.h" + class FThirdwebMultipartFormData { public: diff --git a/Source/Thirdweb/Public/Engine/Marketplace/ThirdwebMarketplaceInternalEntryBase.h b/Source/Thirdweb/Public/Engine/Marketplace/ThirdwebMarketplaceInternalEntryBase.h index 741de58..1f13775 100644 --- a/Source/Thirdweb/Public/Engine/Marketplace/ThirdwebMarketplaceInternalEntryBase.h +++ b/Source/Thirdweb/Public/Engine/Marketplace/ThirdwebMarketplaceInternalEntryBase.h @@ -34,7 +34,7 @@ struct THIRDWEB_API FThirdwebMarketplaceInternalEntryBase FThirdwebAsset Asset; UPROPERTY(BlueprintReadWrite, Category="Core") - EThirdwebMarketplaceListingStatus Status; + EThirdwebMarketplaceListingStatus Status = EThirdwebMarketplaceListingStatus::Max; UPROPERTY(BlueprintReadWrite, Category="Core") FDateTime StartTimeInSeconds; diff --git a/Source/Thirdweb/Public/Internal/ThirdwebHeaders.h b/Source/Thirdweb/Public/Internal/ThirdwebHeaders.h index a928f1c..56965f7 100644 --- a/Source/Thirdweb/Public/Internal/ThirdwebHeaders.h +++ b/Source/Thirdweb/Public/Internal/ThirdwebHeaders.h @@ -2,7 +2,12 @@ #pragma once -class IHttpRequest; +#include "Containers/Array.h" +#include "Containers/Map.h" +#include "Containers/UnrealString.h" +#include "Interfaces/IHttpRequest.h" +#include "Templates/SharedPointer.h" +#include "Templates/Tuple.h" class FThirdwebHeaders { @@ -10,9 +15,12 @@ class FThirdwebHeaders // Constructor FThirdwebHeaders(); - // Adds a header + // Adds a header conditionally void Set(const FString& Name, const FString& Value, const bool bCondition = true); + // Adds headers + void SetMany(TArray> Pairs); + // Removes a header bool Remove(const FString& Name); diff --git a/Source/Thirdweb/Public/Internal/ThirdwebURLSearchParams.h b/Source/Thirdweb/Public/Internal/ThirdwebURLSearchParams.h index fdcd784..8444ad2 100644 --- a/Source/Thirdweb/Public/Internal/ThirdwebURLSearchParams.h +++ b/Source/Thirdweb/Public/Internal/ThirdwebURLSearchParams.h @@ -2,6 +2,10 @@ #pragma once +#include "Containers/Array.h" +#include "Containers/Map.h" +#include "Containers/UnrealString.h" + struct FThirdwebURLSearchParams { FThirdwebURLSearchParams() = default; diff --git a/Source/Thirdweb/Public/ThirdwebRuntimeSettings.h b/Source/Thirdweb/Public/ThirdwebRuntimeSettings.h index 9ce1e6c..2ab60c8 100644 --- a/Source/Thirdweb/Public/ThirdwebRuntimeSettings.h +++ b/Source/Thirdweb/Public/ThirdwebRuntimeSettings.h @@ -38,7 +38,7 @@ class THIRDWEB_API UThirdwebRuntimeSettings : public UDeveloperSettings /** Ecosystem Wallet Identifier tied to your Thirdweb Ecosystem account. Only relevant when using Ecosystem Wallets. e.g. `ecosystem.my-cool-game` */ UPROPERTY(Config, EditAnywhere, DisplayName="Ecosystem ID", Category="Wallets|Ecosystem") FString EcosystemId; - + /** Ecosystem Wallet Partner ID - Needed if Allowlist-based Ecosystem */ UPROPERTY(Config, EditAnywhere, DisplayName="Partner ID", Category="Wallets|Ecosystem") FString PartnerId; @@ -46,7 +46,7 @@ class THIRDWEB_API UThirdwebRuntimeSettings : public UDeveloperSettings /** Required if using custom auth methods via standard InApp wallets (Non-Ecosystem) */ UPROPERTY(Config, EditAnywhere, DisplayName="Encrypton Key (Legacy)", Category="Wallets|Ecosystem|Advanced") FString EncryptionKey; - + /** Optional array of engine signers stored globally for convenience */ UPROPERTY(Config, EditAnywhere, Category="Wallets|Smart") TArray EngineSigners; @@ -58,7 +58,7 @@ class THIRDWEB_API UThirdwebRuntimeSettings : public UDeveloperSettings /** Access Token for Engine Authorization */ UPROPERTY(Config, EditAnywhere, DisplayName="Access Token", meta=(ConfigHierarchyEditable), Category=Engine) FString EngineAccessToken; - + /** Opt in or out of connect analytics */ UPROPERTY(Config, EditAnywhere, Category=Advanced) bool bSendAnalytics; @@ -66,7 +66,7 @@ class THIRDWEB_API UThirdwebRuntimeSettings : public UDeveloperSettings /** Edit Condition for overriding Custom Application Schema */ UPROPERTY(Config, EditAnywhere, DisplayName="Override App URI", meta=(InlineEditConditionToggle, ConfigHierarchyEditable), Category="Advanced|Wallets") bool bOverrideAppUri; - + /** Custom Application URI for oauth redirects. default is {bundleId}://{clientId} */ UPROPERTY(Config, EditAnywhere, DisplayName="Custom App URI", meta=(EditCondition="bOverrideAppUri", ConfigHierarchyEditable), Category="Advanced|Wallets") FString CustomAppUri; @@ -74,21 +74,21 @@ class THIRDWEB_API UThirdwebRuntimeSettings : public UDeveloperSettings /** Edit Condition for overriding the final page shown after auth is complete */ UPROPERTY(Config, EditAnywhere, meta=(InlineEditConditionToggle, ConfigHierarchyEditable), Category="Advanced|Wallets") bool bOverrideExternalAuthRedirectUri; - + /** Custom URI shown after external auth is complete */ UPROPERTY(Config, EditAnywhere, DisplayName="Custom External Auth Redirect URI", meta=(EditCondition="bOverrideExternalAuthRedirectUri", ConfigHierarchyEditable), Category="Advanced|Wallets") FString CustomExternalAuthRedirectUri; - + /** Edit Condition for overriding OAuth Browser Provider Backends */ UPROPERTY(Config, EditAnywhere, meta=(InlineEditConditionToggle, ConfigHierarchyEditable), Category="Advanced|Wallets") bool bOverrideOAuthBrowserProviderBackends; UPROPERTY(Config, EditAnywhere, meta=(EditCondition="bOverrideOAuthBrowserProviderBackends", ArraySizeEnum="EThirdwebOAuthProvider", ConfigHierarchyEditable), Category="Advanced|Wallets") EThirdwebOAuthBrowserBackend OAuthBrowserProviderBackendOverrides[static_cast(EThirdwebOAuthProvider::None)]; - + private: static const TArray ExternalOnlyProviders; - + public: // UFUNCTION(CallInEditor, Category="Wallets|InApp") void GenerateEncryptionKey(); @@ -96,6 +96,9 @@ class THIRDWEB_API UThirdwebRuntimeSettings : public UDeveloperSettings UFUNCTION(BlueprintPure, Category="Thirdweb|Settings") static TArray GetEngineSigners(); + UFUNCTION(BlueprintPure, meta=(ReturnDisplayName="Signer"), Category="Thirdweb|Settings") + static FString UseEngineSigner(UPARAM(meta=(GetOptions="GetEngineSigners")) FString Address) { return Address; } + /** Gets the first engine signer in the array, if any */ UFUNCTION(BlueprintPure, Category="Thirdweb|Settings", meta=(ReturnDisplayName="Signer")) static FString GetEngineSigner(); @@ -103,11 +106,11 @@ class THIRDWEB_API UThirdwebRuntimeSettings : public UDeveloperSettings /** Static accessor to get EncryptionKey */ UFUNCTION(BlueprintPure, DisplayName="Get External Auth Redirect URI", Category="Thirdweb|Settings") static FString GetExternalAuthRedirectUri(); - + /** Static accessor to get EncryptionKey */ UFUNCTION(BlueprintPure, Category="Thirdweb|Settings") static FString GetEncryptionKey(); - + /** Static accessor to retrieve the absolute path of the thirdweb InAppWallet platform */ static FString GetStorageDirectory(); @@ -126,22 +129,22 @@ class THIRDWEB_API UThirdwebRuntimeSettings : public UDeveloperSettings /** Static accessor to get ClientId */ static FString GetClientId(); - + /** Static accessor to get BundleId */ static FString GetBundleId(); - + /** Static accessor to check Analytics Opt-In status */ static bool AnalyticsEnabled(); /** Static accessor to get BaseEngineUrl */ static FString GetEngineBaseUrl(); - + /** Static accessor to get AccessToken */ static FString GetEngineAccessToken(); /** Static accessor for AppUri */ static FString GetAppUri(); - + /** Convenience Getter */ UFUNCTION(BlueprintPure, DisplayName="Get Thirdweb Runtime Settings", meta=(ReturnDisplayName="Settings"), Category="Thirdweb|Settings") static const UThirdwebRuntimeSettings* Get() { return GetDefault(); } @@ -149,7 +152,7 @@ class THIRDWEB_API UThirdwebRuntimeSettings : public UDeveloperSettings protected: // UFUNCTION(CallInEditor, Category="Wallets|Smart") void FetchEngineSigners(); - + private: static const FString DefaultExternalAuthRedirectUri; }; diff --git a/Source/Thirdweb/Thirdweb.Build.cs b/Source/Thirdweb/Thirdweb.Build.cs index b8cfa3b..6152c40 100644 --- a/Source/Thirdweb/Thirdweb.Build.cs +++ b/Source/Thirdweb/Thirdweb.Build.cs @@ -22,9 +22,13 @@ public class Thirdweb : ModuleRules private bool IsMobile => IsIOSIsh || IsAndroid; - private string LibExt => - IsWin64 ? ".lib" : Target.Architectures.Contains(UnrealArch.IOSSimulator) ? ".sim.a" : ".a"; + private string LibExt => IsWin64 ? ".lib" : +#if UE_5_2_OR_LATER + Target.Architectures.Contains(UnrealArch.IOSSimulator) ? ".sim.a" : +#endif + ".a"; + private string LibDir => Path.Combine(Path.Combine(ModuleDirectory, "..", "ThirdParty"), IsIOSIsh ? "IOS" : Target.Platform.ToString()); diff --git a/Source/ThirdwebEditor/Private/Checks/ITWECheck.cpp b/Source/ThirdwebEditor/Private/Checks/ITWECheck.cpp new file mode 100644 index 0000000..b7f2d90 --- /dev/null +++ b/Source/ThirdwebEditor/Private/Checks/ITWECheck.cpp @@ -0,0 +1,5 @@ +// Copyright (c) 2024 Thirdweb. All Rights Reserved. + +#include "Checks/ITWECheck.h" + +const TArray ITWECheck::EmptyEntries = TArray(); diff --git a/Source/ThirdwebEditor/Private/Checks/TWECheckEngine.cpp b/Source/ThirdwebEditor/Private/Checks/TWECheckEngine.cpp new file mode 100644 index 0000000..cebaedb --- /dev/null +++ b/Source/ThirdwebEditor/Private/Checks/TWECheckEngine.cpp @@ -0,0 +1,326 @@ +// Copyright (c) 2024 Thirdweb. All Rights Reserved. + +#include "Checks/TWECheckEngine.h" + +#include "ILevelEditor.h" +#include "LevelEditor.h" +#include "SLevelViewport.h" +#include "Checks/TWEEssentialSettingsCheck.h" +#include "Modules/ModuleManager.h" +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/SWidget.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Text/STextBlock.h" + +FTWECheckEngine::FTWECheckEngine() +{ + InjectedNotificationBar = SNew(SVerticalBox); + RegisteredChecks.Add(MakeShared()); +} + +FTWECheckEngine::~FTWECheckEngine() +{ + if (CurrentlyInjectedIntoWidget.IsValid()) + { + CurrentlyInjectedIntoWidget.Pin()->RemoveSlot(InjectedNotificationBar.ToSharedRef()); + InjectedNotificationBar.Reset(); + CurrentlyInjectedIntoWidget.Reset(); + } +} + +FSlateColor FTWECheckEngine::GetNotificationBackgroundColor() const +{ + return FSlateColor(FLinearColor(0.89627f, 0.799103f, 0.219526f, 1.0f)); +} + +FSlateColor FTWECheckEngine::GetNotificationButtonOutlineColor() const +{ + return FSlateColor(FLinearColor(202.f / 255.f, 187.f / 255.f, 80.f / 255.f, 1.0f)); +} + +FSlateColor FTWECheckEngine::GetNotificationButtonBackgroundColor() const +{ + return FSlateColor(FLinearColor(255.f / 255.f, 244.f / 255.f, 143.f / 255.f, 1.0f)); +} + +FSlateColor FTWECheckEngine::GetNotificationButtonBackgroundHoveredColor() const +{ + return FSlateColor(FLinearColor(1.0f, 0.921569f, 0.392157f, 1.0f)); +} + +FSlateColor FTWECheckEngine::GetNotificationButtonBackgroundPressedColor() const +{ + return FSlateColor(FLinearColor(0.947307f, 0.854993f, 0.246201f, 1.0f)); +} + +FSlateColor FTWECheckEngine::GetNotificationFontColor() const +{ + return FSlateColor(FLinearColor(0.0f, 0.0f, 0.0f, 1.0f)); +} + +const FButtonStyle* FTWECheckEngine::GetNotificationButtonStyle() const +{ + auto Self = const_cast(this); + Self->NotificationButtonStyle = FCoreStyle::Get().GetWidgetStyle("Button"); + Self->NotificationButtonStyle.Normal.OutlineSettings.Color = GetNotificationButtonOutlineColor(); + Self->NotificationButtonStyle.Hovered.OutlineSettings.Color = GetNotificationButtonOutlineColor(); + Self->NotificationButtonStyle.Pressed.OutlineSettings.Color = GetNotificationButtonOutlineColor(); + Self->NotificationButtonStyle.Normal.TintColor = GetNotificationButtonBackgroundColor(); + Self->NotificationButtonStyle.Hovered.TintColor = GetNotificationButtonBackgroundHoveredColor(); + Self->NotificationButtonStyle.Pressed.TintColor = GetNotificationButtonBackgroundPressedColor(); + return &(Self->NotificationButtonStyle); +} + +const FSlateBrush* FTWECheckEngine::GetNotificationBackgroundBrush() const +{ + auto Self = const_cast(this); + Self->NotificationBackgroundBrush = FSlateBrush(); + Self->NotificationBackgroundBrush.ImageSize = FVector2D(32, 32); + Self->NotificationBackgroundBrush.Margin = FMargin(); + Self->NotificationBackgroundBrush.TintColor = FLinearColor::White; + Self->NotificationBackgroundBrush.DrawAs = ESlateBrushDrawType::Image; + Self->NotificationBackgroundBrush.Tiling = ESlateBrushTileType::NoTile; + Self->NotificationBackgroundBrush.Mirroring = ESlateBrushMirrorType::NoMirror; + Self->NotificationBackgroundBrush.ImageType = ESlateBrushImageType::NoImage; + return &(Self->NotificationBackgroundBrush); +} + +TSharedRef FTWECheckEngine::CreateNewNotification(const FTWECheckEntry& Entry) +{ + auto HorizontalBox = SNew(SHorizontalBox); + HorizontalBox->AddSlot().FillWidth(1).VAlign( + VAlign_Center)[SNew(STextBlock) + .Text(FText::FromString(Entry.GetCheckMessage())) + .AutoWrapText(true) + .ColorAndOpacity(this, &FTWECheckEngine::GetNotificationFontColor)]; + for (const auto& Action : Entry.GetCheckActions()) + { + HorizontalBox->AddSlot() + .AutoWidth() + .Padding(FMargin(10, 0, 0, 0)) + .VAlign(EVerticalAlignment::VAlign_Center) + [SNew(SButton) + .ButtonStyle(FTWECheckEngine::GetNotificationButtonStyle()) + .OnClicked(FOnClicked::CreateSP( + this, + &FTWECheckEngine::OnActionClicked, + Entry.GetCheckId(), + Action + .GetActionId())) + [ + SNew(STextBlock) + .Text(FText::FromString(Action.GetActionDisplayName())) + .ColorAndOpacity(this, &FTWECheckEngine::GetNotificationFontColor) + ]]; + } + HorizontalBox->AddSlot() + .AutoWidth() + .Padding(FMargin(10, 0, 0, 0)) + .VAlign(EVerticalAlignment::VAlign_Center) + [SNew(SButton) + .ButtonStyle(FTWECheckEngine::GetNotificationButtonStyle()) + .OnClicked(FOnClicked::CreateSP(this, &FTWECheckEngine::OnDismissClicked, Entry.GetCheckId())) + [ + SNew(STextBlock) + .Text(FText::FromString("Dismiss")) + .ColorAndOpacity(this, &FTWECheckEngine::GetNotificationFontColor) + ]]; + + return SNew(SBorder) + .BorderBackgroundColor(this, &FTWECheckEngine::GetNotificationBackgroundColor) + .BorderImage(this, &FTWECheckEngine::GetNotificationBackgroundBrush) + [ + HorizontalBox + ] + .Padding(FMargin(10, 5)); +} + +FReply FTWECheckEngine::OnActionClicked( + // NOLINTNEXTLINE(performance-unnecessary-value-param) + FString CheckId, + // NOLINTNEXTLINE(performance-unnecessary-value-param) + FString ActionId) +{ + if (!CurrentNotifications.Contains(CheckId)) + { + return FReply::Handled(); + } + + CurrentNotifications[CheckId].Value->HandleAction(CheckId, ActionId); + + InjectedNotificationBar->RemoveSlot(CurrentNotifications[CheckId].Key.ToSharedRef()); + CurrentNotifications.Remove(CheckId); + + return FReply::Handled(); +} + +FReply FTWECheckEngine::OnDismissClicked(FString CheckId) +{ + if (!CurrentNotifications.Contains(CheckId)) + { + return FReply::Handled(); + } + + InjectedNotificationBar->RemoveSlot(CurrentNotifications[CheckId].Key.ToSharedRef()); + CurrentNotifications.Remove(CheckId); + + DismissedCheckIds.Add(CheckId); + + return FReply::Handled(); +} + +void FTWECheckEngine::Tick(float DeltaSeconds) +{ + TSet DismissedCheckIdsToForget = DismissedCheckIds; + for (const auto& Check : RegisteredChecks) + { + if (Check->ShouldTick()) + { + auto Results = Check->Tick(DeltaSeconds); + if (Results.Num() > 0) + { + for (const auto& Entry : Results) + { + if (!CurrentNotifications.Contains(Entry.GetCheckId())) + { + if (DismissedCheckIds.Contains(Entry.GetCheckId())) + { + DismissedCheckIdsToForget.Remove(Entry.GetCheckId()); + } + else + { + TSharedRef Notification = CreateNewNotification(Entry); + CurrentNotifications.Add( + Entry.GetCheckId(), + TTuple, TSharedPtr>(Notification, Check)); + InjectedNotificationBar->AddSlot() + .Padding(FMargin(0, 0, 0, 1)) + .AutoHeight() + .AttachWidget(Notification); + } + } + } + } + } + } + for (const auto& CheckId : DismissedCheckIdsToForget) + { + DismissedCheckIds.Remove(CheckId); + } + + if (FLevelEditorModule* LevelEditorModule = FModuleManager::GetModulePtr(TEXT("LevelEditor"))) + { + if (TWeakPtr LevelEditor = LevelEditorModule->GetLevelEditorInstance(); LevelEditor.IsValid()) + { + if (TSharedPtr PinnedLevelEditor = LevelEditor.Pin(); PinnedLevelEditor.IsValid()) + { + TSharedPtr CurrentActiveLevelViewport = PinnedLevelEditor->GetActiveViewportInterface(); + if (!CurrentActiveLevelViewport.IsValid()) + { + if (PinnedLevelEditor->GetViewports().Num() > 0) + { + CurrentActiveLevelViewport = PinnedLevelEditor->GetViewports()[0]; + } + } + + bool bRebuildInjectedLocation = false; + if ((CurrentActiveLevelViewport.IsValid() && !ActiveLevelViewport.IsValid()) || + (CurrentActiveLevelViewport.Get() != ActiveLevelViewport.Pin().Get())) + { + bRebuildInjectedLocation = true; + ActiveLevelViewport = CurrentActiveLevelViewport; + } + + if (bRebuildInjectedLocation) + { + TSharedPtr CurrentWidget = StaticCastSharedPtr(CurrentActiveLevelViewport); + while (CurrentWidget.IsValid() && !CurrentWidget->GetType().IsEqual(FName(TEXT("SVerticalBox"))) && + CurrentWidget->IsParentValid()) + { + CurrentWidget = CurrentWidget->GetParentWidget(); + } + + if (CurrentWidget.IsValid() && CurrentWidget->GetType().IsEqual(FName(TEXT("SVerticalBox")))) + { + if (TSharedPtr VerticalBox = StaticCastSharedPtr(CurrentWidget); VerticalBox.IsValid()) + { + if (VerticalBox->GetChildren()->Num() == 2) + { + if (CurrentlyInjectedIntoWidget.IsValid()) + { + CurrentlyInjectedIntoWidget.Pin()->RemoveSlot( + InjectedNotificationBar.ToSharedRef()); + CurrentlyInjectedIntoWidget.Reset(); + } + + VerticalBox->InsertSlot(1) + .AutoHeight() + .Padding(FMargin(0, 0, 0, 1)) + .AttachWidget(InjectedNotificationBar.ToSharedRef()); + CurrentlyInjectedIntoWidget = VerticalBox; + } + } + } + } + } + } + } +} + +void FTWECheckEngine::ProcessLogMessage(const FString& InLogLevel, const FString& Category, const FString& Message) +{ + for (const auto& Check : RegisteredChecks) + { + auto Results = Check->ProcessLogMessage(InLogLevel, Category, Message); + if (Results.Num() > 0) + { + for (const auto& Entry : Results) + { + if (!CurrentNotifications.Contains(Entry.GetCheckId())) + { + // We don't persist dismissals for log-based checks, since they aren't persistently triggered every + // tick. + DismissedCheckIds.Remove(Entry.GetCheckId()); + + TSharedRef Notification = CreateNewNotification(Entry); + CurrentNotifications.Add( + Entry.GetCheckId(), + TTuple, TSharedPtr>(Notification, Check)); + InjectedNotificationBar->AddSlot() + .Padding(FMargin(0, 0, 0, 1)) + .AutoHeight() + .AttachWidget(Notification); + } + } + } + } +} + +void FTWECheckEngine::ProcessCustomSignal(const FString& Context, const FString& SignalId) +{ + for (const auto& Check : RegisteredChecks) + { + auto Results = Check->ProcessCustomSignal(Context, SignalId); + if (Results.Num() > 0) + { + for (const auto& Entry : Results) + { + if (!CurrentNotifications.Contains(Entry.GetCheckId())) + { + // We don't persist dismissals for log-based checks, since they aren't persistently triggered every tick. + DismissedCheckIds.Remove(Entry.GetCheckId()); + + TSharedRef Notification = CreateNewNotification(Entry); + CurrentNotifications.Add( + Entry.GetCheckId(), + TTuple, TSharedPtr>(Notification, Check)); + InjectedNotificationBar->AddSlot() + .Padding(FMargin(0, 0, 0, 1)) + .AutoHeight() + .AttachWidget(Notification); + } + } + } + } +} diff --git a/Source/ThirdwebEditor/Private/Checks/TWEEssentialSettingsCheck.cpp b/Source/ThirdwebEditor/Private/Checks/TWEEssentialSettingsCheck.cpp new file mode 100644 index 0000000..6134f77 --- /dev/null +++ b/Source/ThirdwebEditor/Private/Checks/TWEEssentialSettingsCheck.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2024 Thirdweb. All Rights Reserved. + +#include "Checks/TWEEssentialSettingsCheck.h" + +#include "ISettingsModule.h" +#include "ThirdwebEditorUtils.h" +#include "ThirdwebRuntimeSettings.h" +#include "UnrealEdMisc.h" +#include "Framework/Docking/TabManager.h" +#include "Widgets/Docking/SDockTab.h" + +TArray FTWEEssentialSettingsCheck::Tick(float DeltaSeconds) const +{ + // ReSharper disable once CppTooWideScopeInitStatement + TSharedPtr ExistingProjectSettingsTab = FGlobalTabmanager::Get()->FindExistingLiveTab(FName("ProjectSettings")); + if (!ExistingProjectSettingsTab.IsValid() || (!ExistingProjectSettingsTab->IsActive() && !ExistingProjectSettingsTab->IsForeground())) + { + if (UThirdwebRuntimeSettings::GetClientId().IsEmpty() || UThirdwebRuntimeSettings::GetBundleId().IsEmpty()) + { + return TArray{ + FTWECheckEntry( + "FTWEEssentialSettingsCheck::Misconfigured", + "You are missing essential settings that are required for ThirdwebSDK to work. You must set values for at least \"Client ID\" and \"Bundle ID\".", + TArray{ + FTWECheckAction("FTWEEssentialSettingsCheck::OpenProjectSettings", "Open settings"), + FTWECheckAction("FTWEEssentialSettingsCheck::OpenDocumentation", "Read documentation") + }) + }; + } + } + + return EmptyEntries; +} + +void FTWEEssentialSettingsCheck::HandleAction(const FString& CheckId, const FString& ActionId) const +{ + if (CheckId == "FTWEEssentialSettingsCheck::Misconfigured" || + CheckId == "FTWEEssentialSettingsCheck::EditorRestartRequired") + { + if (ActionId == "FTWEEssentialSettingsCheck::OpenProjectSettings") + { + ThirdwebEditorUtils::OpenDeveloperSettings(); + } + else if (ActionId == "FTWEEssentialSettingsCheck::RestartEditor") + { + FUnrealEdMisc::Get().RestartEditor(false); + } + else if (ActionId == "FTWEEssentialSettingsCheck::OpenDocumentation") + { + FPlatformProcess::LaunchURL(TEXT("https://portal.thirdweb.com/unreal-engine/getting-started"), nullptr, nullptr); + } + } +} diff --git a/Source/ThirdwebEditor/Private/ThirdwebEditorCommands.cpp b/Source/ThirdwebEditor/Private/ThirdwebEditorCommands.cpp index 62f006d..2575133 100644 --- a/Source/ThirdwebEditor/Private/ThirdwebEditorCommands.cpp +++ b/Source/ThirdwebEditor/Private/ThirdwebEditorCommands.cpp @@ -2,8 +2,6 @@ #include "ThirdwebEditorCommands.h" -#include "ThirdwebEditorSettings.h" - #define LOCTEXT_NAMESPACE "FThirdwebEditorModule" FThirdwebEditorCommands::FThirdwebEditorCommands(): TCommands( @@ -17,8 +15,21 @@ FThirdwebEditorCommands::FThirdwebEditorCommands(): TCommands( void FThirdwebEditorCommands::RegisterCommands() { - UI_COMMAND(OpenSettings, "Thirdweb Settings", "Open Thirdweb settings", EUserInterfaceActionType::Button, FInputChord()); - UI_COMMAND(TakeScreenshot, "Take Screenshot", "Take a screenshot of the currently selected blueprint nodes", EUserInterfaceActionType::Button, UThirdwebEditorSettings::GetScreenshotShortcut()); + // Configuration + UI_COMMAND(OpenRuntimeSettings, "Project Settings...", "Edit core SDK runtime settings.", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(OpenEditorSettings, "Editor Preferences...", "Edit optional developer settings.", EUserInterfaceActionType::Button, FInputChord()); + // Utilities + UI_COMMAND(TakeScreenshot, "Take Screenshot", "Take a screenshot of the currently selected blueprint nodes.", EUserInterfaceActionType::Button, FInputChord()); + // Reference + UI_COMMAND(ViewDocumentation, "View Documentation", "View the plugin documentation.", EUserInterfaceActionType::Button, FInputChord()); + // Support + UI_COMMAND(AccessOfficialSupport, "Official Support", "Get support from thirdweb.", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(AccessCommunitySupport, "Community Support", "Get support from the community.", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(ReportABug, "Report a Bug", "Found a bug? Let us know about it!", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(IssueTracker, "Issue Tracker", "Check the current status of public bugs and other issues.", EUserInterfaceActionType::Button, FInputChord()); + // Footer + UI_COMMAND(ViewWebsite, "Visit thirdweb.com", "Open the website for Thirdweb.", EUserInterfaceActionType::Button, FInputChord()); + } #undef LOCTEXT_NAMESPACE diff --git a/Source/ThirdwebEditor/Private/ThirdwebEditorModule.cpp b/Source/ThirdwebEditor/Private/ThirdwebEditorModule.cpp index 7fd3586..7732338 100644 --- a/Source/ThirdwebEditor/Private/ThirdwebEditorModule.cpp +++ b/Source/ThirdwebEditor/Private/ThirdwebEditorModule.cpp @@ -9,9 +9,12 @@ #include "ThirdwebEditorScreenshotUtils.h" #include "ThirdwebEditorSettings.h" #include "ThirdwebEditorStyle.h" +#include "ThirdwebEditorUtils.h" #include "ThirdwebRuntimeSettings.h" #include "ToolMenus.h" #include "Algo/ForEach.h" +#include "Checks/TWECheckEngine.h" +#include "Internationalization/Text.h" #include "Misc/MessageDialog.h" #define LOCTEXT_NAMESPACE "FThirdwebEditorModule" @@ -20,60 +23,262 @@ void FThirdwebEditorModule::StartupModule() { FThirdwebEditorStyle::Initialize(); FThirdwebEditorStyle::ReloadTextures(); - FThirdwebEditorCommands::Register(); CommandList = MakeShareable(new FUICommandList); - + + // Configuration + CommandList->MapAction( + FThirdwebEditorCommands::Get().OpenRuntimeSettings, + FExecuteAction::CreateStatic(&FThirdwebEditorModule::OpenRuntimeSettings), + FCanExecuteAction() + ); + CommandList->MapAction( + FThirdwebEditorCommands::Get().OpenEditorSettings, + FExecuteAction::CreateStatic(&FThirdwebEditorModule::OpenEditorSettings), + FCanExecuteAction() + ); + // Utilities CommandList->MapAction( - FThirdwebEditorCommands::GetOpenSettingsCommand(), - FExecuteAction::CreateStatic(&FThirdwebEditorModule::OpenSettingsButtonClicked), + FThirdwebEditorCommands::Get().TakeScreenshot, + FExecuteAction::CreateStatic(&FThirdwebEditorModule::TakeScreenshot), FCanExecuteAction() ); + // Reference CommandList->MapAction( - FThirdwebEditorCommands::GetTakeScreenshotCommand(), - FExecuteAction::CreateStatic(&FThirdwebEditorModule::TakeScreenshotButtonClicked), + FThirdwebEditorCommands::Get().ViewDocumentation, + FExecuteAction::CreateStatic(&FThirdwebEditorModule::ViewDocumentation), + FCanExecuteAction() + ); + // Support + CommandList->MapAction( + FThirdwebEditorCommands::Get().AccessOfficialSupport, + FExecuteAction::CreateStatic(&FThirdwebEditorModule::AccessOfficialSupport), + FCanExecuteAction() + ); + CommandList->MapAction( + FThirdwebEditorCommands::Get().AccessCommunitySupport, + FExecuteAction::CreateStatic(&FThirdwebEditorModule::AccessCommunitySupport), + FCanExecuteAction() + ); + CommandList->MapAction( + FThirdwebEditorCommands::Get().ReportABug, + FExecuteAction::CreateStatic(&FThirdwebEditorModule::ReportABug), + FCanExecuteAction() + ); + CommandList->MapAction( + FThirdwebEditorCommands::Get().IssueTracker, + FExecuteAction::CreateStatic(&FThirdwebEditorModule::IssueTracker), + FCanExecuteAction() + ); + // Footer + CommandList->MapAction( + FThirdwebEditorCommands::Get().ViewWebsite, + FExecuteAction::CreateStatic(&FThirdwebEditorModule::ViewWebsite), FCanExecuteAction() ); UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FThirdwebEditorModule::RegisterMenus)); + + CheckEngine = MakeShared(); + + // Register ticker. + TickerHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FThirdwebEditorModule::Tick)); } void FThirdwebEditorModule::ShutdownModule() { + FTSTicker::GetCoreTicker().RemoveTicker(TickerHandle); + TickerHandle.Reset(); UToolMenus::UnRegisterStartupCallback(this); UToolMenus::UnregisterOwner(this); - FThirdwebEditorStyle::Shutdown(); - FThirdwebEditorCommands::Unregister(); } -void FThirdwebEditorModule::OpenSettingsButtonClicked() +bool FThirdwebEditorModule::Tick(float DeltaSeconds) +{ + CheckEngine->Tick(DeltaSeconds); + return true; +} + +void FThirdwebEditorModule::ProcessLogMessage(const FString& InLogLevel, const FString& Category, const FString& Message) { - const UThirdwebRuntimeSettings* Settings = UThirdwebRuntimeSettings::Get(); - FModuleManager::LoadModuleChecked("Settings").ShowViewer(Settings->GetContainerName(), Settings->GetCategoryName(), Settings->GetSectionName()); + CheckEngine->ProcessLogMessage(InLogLevel, Category, Message); } -void FThirdwebEditorModule::TakeScreenshotButtonClicked() +void FThirdwebEditorModule::ProcessCustomSignal(const FString& Context, const FString& SignalId) +{ + CheckEngine->ProcessCustomSignal(Context, SignalId); +} + +void FThirdwebEditorModule::OpenRuntimeSettings() +{ + ThirdwebEditorUtils::OpenDeveloperSettings(); +} + +void FThirdwebEditorModule::OpenEditorSettings() +{ + ThirdwebEditorUtils::OpenDeveloperSettings(); +} + +void FThirdwebEditorModule::ReportABug() +{ + FPlatformProcess::LaunchURL(TEXT("https://github.com/thirdweb-dev/unreal-engine/issues/new/choose"), nullptr, nullptr); +} + +void FThirdwebEditorModule::IssueTracker() +{ + FPlatformProcess::LaunchURL(TEXT("https://github.com/thirdweb-dev/unreal-engine/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen"), nullptr, nullptr); +} + +void FThirdwebEditorModule::TakeScreenshot() { FThirdwebEditorScreenshotUtils::TakeScreenshot(); } void FThirdwebEditorModule::RegisterMenus() { + TSharedPtr AccessOfficialSupport; + TSharedPtr AccessCommunitySupport; + TSharedPtr ReportABug; + TSharedPtr IssueTracker; + // Footer + TSharedPtr ViewWebsite; // Owner will be used for cleanup in call to UToolMenus::UnregisterOwner FToolMenuOwnerScoped OwnerScoped(this); - TSharedPtr Cl = CommandList; { - FToolMenuSection& Section = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar")->FindOrAddSection("ThirdwebSection"); - Section.AddEntry(FToolMenuEntry::InitToolBarButton(FThirdwebEditorCommands::GetOpenSettingsCommand())).SetCommandList(Cl); + UToolMenu* Menu = UToolMenus::Get()->RegisterMenu("LevelEditor.LevelEditorToolBar.LevelToolbarThirdweb"); + + // Configuration + { + FToolMenuSection& Section = Menu->AddSection("ConfigurationSection", LOCTEXT("ConfigurationSection", "Configuration")); + Section.AddMenuEntry( + FThirdwebEditorCommands::Get().OpenRuntimeSettings, + TAttribute(), + TAttribute(), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "ProjectSettings.TabIcon") + ); + Section.AddMenuEntry( + FThirdwebEditorCommands::Get().OpenEditorSettings, + TAttribute(), + TAttribute(), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "EditorPreferences.TabIcon") + ); + } + // Utilities + { + FToolMenuSection& Section = Menu->AddSection("UtilitiesSection", LOCTEXT("UtilitiesSection", "Utilities")); + + Section.AddMenuEntry( + FThirdwebEditorCommands::Get().TakeScreenshot, + TAttribute(), + TAttribute(), + FSlateIcon(FThirdwebEditorStyle::GetStyleSetName(), "LevelEditor.Thirdweb.Screenshot") + ); + } + // Reference + { + FToolMenuSection& Section = Menu->AddSection("ReferenceSection", LOCTEXT("ReferenceSection", "Reference")); + + Section.AddMenuEntry( + FThirdwebEditorCommands::Get().ViewDocumentation, + TAttribute(), + TAttribute(), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Documentation") + ); + } + // Support + { + FToolMenuSection& Section = Menu->AddSection("SupportSection", LOCTEXT("SupportSection", "Support")); + Section.AddMenuEntry( + FThirdwebEditorCommands::Get().AccessOfficialSupport, + TAttribute(), + TAttribute(), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "MainFrame.VisitSupportWebSite") + ); + Section.AddMenuEntry( + FThirdwebEditorCommands::Get().AccessCommunitySupport, + TAttribute(), + TAttribute(), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "MainFrame.VisitCommunityHome") + ); + Section.AddMenuEntry( + FThirdwebEditorCommands::Get().ReportABug, + TAttribute(), + TAttribute(), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "Debug") + ); + Section.AddMenuEntry( + FThirdwebEditorCommands::Get().IssueTracker, + TAttribute(), + TAttribute(), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "MainFrame.OpenIssueTracker") + ); + // Footer + Section.AddSeparator("footer"), + Section.AddMenuEntry( + FThirdwebEditorCommands::Get().ViewWebsite, + TAttribute(), + TAttribute(), + FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.OpenInBrowser") + ); + } } { - FToolMenuSection& Section = UToolMenus::Get()->ExtendMenu("AssetEditor.WidgetBlueprintEditor.ToolBar")->FindOrAddSection("ThirdwebSection"); - FThirdwebEditorCommands::ForEach([&Section, Cl](const TSharedPtr& Ci) { Section.AddEntry(FToolMenuEntry::InitToolBarButton(Ci)).SetCommandList(Cl); }); + UToolMenu* Toolbar = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar"); + { + FToolMenuSection& SettingsSection = Toolbar->AddSection("ThirdwebSettings"); + { + FUIAction Action; + Action.IsActionVisibleDelegate = FIsActionButtonVisible::CreateLambda([]() + { + const UThirdwebEditorSettings* Settings = UThirdwebEditorSettings::Get(); + return !Settings || !Settings->HideDropdownInEditorToolbar(); + }); + FToolMenuEntry ComboButton = FToolMenuEntry::InitComboButton( + "LevelToolbarThirdweb", + Action, + FOnGetContent::CreateStatic( + &FThirdwebEditorModule::GenerateOnlineSettingsMenu, + CommandList.ToSharedRef()), + LOCTEXT("SettingsCombo", "Thirdweb"), + LOCTEXT("SettingsCombo_ToolTip", "Manage the Thirdweb SDK"), + FSlateIcon(FThirdwebEditorStyle::GetStyleSetName(), "LevelEditor.Thirdweb.Logo"), + false, + "LevelToolbarThirdweb"); + ComboButton.StyleNameOverride = "CalloutToolbar"; + SettingsSection.AddEntry(ComboButton); + } + } } - +} + +void FThirdwebEditorModule::ViewDocumentation() +{ + FPlatformProcess::LaunchURL(TEXT("https://portal.thirdweb.com/unreal-engine"), nullptr, nullptr); +} + +void FThirdwebEditorModule::ViewWebsite() +{ + FPlatformProcess::LaunchURL(TEXT("https://thirdweb.com/"), nullptr, nullptr); +} + +void FThirdwebEditorModule::AccessOfficialSupport() +{ + FPlatformProcess::LaunchURL(TEXT("https://thirdweb.com/support"), nullptr, nullptr); +} + +void FThirdwebEditorModule::AccessCommunitySupport() +{ + FPlatformProcess::LaunchURL(TEXT("https://discord.gg/thirdweb"), nullptr, nullptr); +} + +// ReSharper disable once CppPassValueParameterByConstReference +TSharedRef FThirdwebEditorModule::GenerateOnlineSettingsMenu(TSharedRef InCommandList) +{ + FToolMenuContext MenuContext(InCommandList); + return UToolMenus::Get()->GenerateWidget("LevelEditor.LevelEditorToolBar.LevelToolbarThirdweb", MenuContext); } #undef LOCTEXT_NAMESPACE diff --git a/Source/ThirdwebEditor/Private/ThirdwebEditorSettings.cpp b/Source/ThirdwebEditor/Private/ThirdwebEditorSettings.cpp index 2cb033b..9bb82f7 100644 --- a/Source/ThirdwebEditor/Private/ThirdwebEditorSettings.cpp +++ b/Source/ThirdwebEditor/Private/ThirdwebEditorSettings.cpp @@ -17,6 +17,7 @@ UThirdwebEditorSettings::UThirdwebEditorSettings() ScreenshotShortcut = FInputChord(EModifierKey::Control, EKeys::F7); bScreenshotDisableNotifications = false; ScreenshotNotificationTimeoutSeconds = 5; + bHideDropdownInEditorToolbar = false; } #if WITH_EDITOR diff --git a/Source/ThirdwebEditor/Private/ThirdwebEditorStyle.cpp b/Source/ThirdwebEditor/Private/ThirdwebEditorStyle.cpp index 7d92115..9d2696c 100644 --- a/Source/ThirdwebEditor/Private/ThirdwebEditorStyle.cpp +++ b/Source/ThirdwebEditor/Private/ThirdwebEditorStyle.cpp @@ -3,13 +3,9 @@ #include "ThirdwebEditorStyle.h" #include "ThirdwebEditorModule.h" - #include "Framework/Application/SlateApplication.h" - #include "Interfaces/IPluginManager.h" - #include "Slate/SlateGameResources.h" - #include "Styling/SlateStyleMacros.h" #include "Styling/SlateStyleRegistry.h" @@ -48,8 +44,8 @@ TSharedRef FThirdwebEditorStyle::Create() TSharedRef Style = MakeShareable(new FSlateStyleSet("ThirdwebEditorStyle")); Style->SetContentRoot(IPluginManager::Get().FindPlugin("Thirdweb")->GetBaseDir() / TEXT("Resources")); - Style->Set("ThirdwebEditor.OpenSettings", new IMAGE_BRUSH_SVG(TEXT("ThirdwebIcon"), Icon20x20)); - Style->Set("ThirdwebEditor.TakeScreenshot", new IMAGE_BRUSH_SVG(TEXT("ScreenshotIcon"), Icon20x20)); + Style->Set("LevelEditor.Thirdweb.Logo", new IMAGE_BRUSH_SVG(TEXT("ThirdwebIcon"), Icon20x20)); + Style->Set("LevelEditor.Thirdweb.Screenshot", new IMAGE_BRUSH_SVG(TEXT("ScreenshotIcon"), Icon20x20)); return Style; } diff --git a/Source/ThirdwebEditor/Private/ThirdwebEditorUtils.cpp b/Source/ThirdwebEditor/Private/ThirdwebEditorUtils.cpp new file mode 100644 index 0000000..92d1c6e --- /dev/null +++ b/Source/ThirdwebEditor/Private/ThirdwebEditorUtils.cpp @@ -0,0 +1,16 @@ +// Copyright (c) 2024 Thirdweb. All Rights Reserved. + +#include "ThirdwebEditorUtils.h" + +#include "ISettingsModule.h" +#include "Modules/ModuleManager.h" + +namespace ThirdwebEditorUtils +{ + template + void OpenDeveloperSettings() + { + const T* Settings = GetDefault(); + FModuleManager::LoadModuleChecked("Settings").ShowViewer(Settings->GetContainerName(), Settings->GetCategoryName(), Settings->GetSectionName()); + } +} diff --git a/Source/ThirdwebEditor/Public/Checks/ITWECheck.h b/Source/ThirdwebEditor/Public/Checks/ITWECheck.h new file mode 100644 index 0000000..f625790 --- /dev/null +++ b/Source/ThirdwebEditor/Public/Checks/ITWECheck.h @@ -0,0 +1,59 @@ +// Copyright (c) 2024 Thirdweb. All Rights Reserved. + +#pragma once + +#include "Containers/UnrealString.h" + +class FTWECheckAction +{ + FString ActionId; + FString ActionDisplayName; + +public: + FTWECheckAction(const FString& InActionId, const FString& InActionDisplayName): ActionId(InActionId), ActionDisplayName(InActionDisplayName) + { + } + + const FString& GetActionId() const { return ActionId; } + const FString& GetActionDisplayName() const { return ActionDisplayName; } +}; + +class FTWECheckEntry +{ + FString CheckId; + FString CheckMessage; + TArray CheckActions; + +public: + FTWECheckEntry(const FString& InCheckId, const FString& InCheckMessage, const TArray& InCheckActions) + : CheckId(InCheckId), CheckMessage(InCheckMessage), CheckActions(InCheckActions) + { + } + + const FString& GetCheckId() const { return CheckId; } + const FString& GetCheckMessage() const { return CheckMessage; } + const TArray& GetCheckActions() const { return CheckActions; } +}; + +class ITWECheck +{ +protected: + static const TArray EmptyEntries; + +public: + ITWECheck() = default; + UE_NONCOPYABLE(ITWECheck); + + virtual ~ITWECheck() + { + } + + virtual bool ShouldTick() const { return false; } + virtual TArray Tick(float DeltaSeconds) const{return EmptyEntries;} + + virtual TArray ProcessCustomSignal(const FString& Context, const FString& SignalId) const {return EmptyEntries;} + virtual TArray ProcessLogMessage(FString InLogLevel, const FString& Category, const FString& Message) const { return EmptyEntries; } + virtual void HandleAction(const FString& CheckId, const FString& ActionId) const + { + } +}; diff --git a/Source/ThirdwebEditor/Public/Checks/TWECheckEngine.h b/Source/ThirdwebEditor/Public/Checks/TWECheckEngine.h new file mode 100644 index 0000000..28d3720 --- /dev/null +++ b/Source/ThirdwebEditor/Public/Checks/TWECheckEngine.h @@ -0,0 +1,49 @@ +// Copyright (c) 2024 Thirdweb. All Rights Reserved. + +#pragma once + +#include "ITWECheck.h" +#include "Styling/SlateBrush.h" +#include "Styling/SlateColor.h" +#include "Styling/SlateTypes.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Layout/SBorder.h" + +class FTWECheckEngine : public TSharedFromThis +{ + // These widgets are managed by the level editor, so must be TWeakPtr to allow level editor UI to be freed correctly. + TWeakPtr ActiveLevelViewport; + TWeakPtr CurrentlyInjectedIntoWidget; + + TSharedPtr InjectedNotificationBar; + + FButtonStyle NotificationButtonStyle; + FSlateBrush NotificationBackgroundBrush; + + FSlateColor GetNotificationBackgroundColor() const; + FSlateColor GetNotificationButtonOutlineColor() const; + FSlateColor GetNotificationButtonBackgroundColor() const; + FSlateColor GetNotificationButtonBackgroundHoveredColor() const; + FSlateColor GetNotificationButtonBackgroundPressedColor() const; + FSlateColor GetNotificationFontColor() const; + const FButtonStyle* GetNotificationButtonStyle() const; + const FSlateBrush* GetNotificationBackgroundBrush() const; + + TSharedRef CreateNewNotification(const FTWECheckEntry& Entry); + FReply OnActionClicked(FString CheckId, FString ActionId); + FReply OnDismissClicked(FString CheckId); + + TMap, TSharedPtr>> CurrentNotifications; + + TArray> RegisteredChecks; + TSet DismissedCheckIds; + +public: + FTWECheckEngine(); + UE_NONCOPYABLE(FTWECheckEngine); + virtual ~FTWECheckEngine(); + + void Tick(float DeltaSeconds); + void ProcessLogMessage(const FString& InLogLevel, const FString& Category, const FString& Message); + void ProcessCustomSignal(const FString& Context, const FString& SignalId); +}; diff --git a/Source/ThirdwebEditor/Public/Checks/TWEEssentialSettingsCheck.h b/Source/ThirdwebEditor/Public/Checks/TWEEssentialSettingsCheck.h new file mode 100644 index 0000000..d1e5cf4 --- /dev/null +++ b/Source/ThirdwebEditor/Public/Checks/TWEEssentialSettingsCheck.h @@ -0,0 +1,25 @@ +// Copyright (c) 2024 Thirdweb. All Rights Reserved. + +#pragma once + +#include "ITWECheck.h" + +class UThirdwebRuntimeSettings; + +class FTWEEssentialSettingsCheck : public ITWECheck +{ +public: + FTWEEssentialSettingsCheck() + { + } + + UE_NONCOPYABLE(FTWEEssentialSettingsCheck); + + virtual ~FTWEEssentialSettingsCheck() + { + } + + virtual bool ShouldTick() const override { return true; } + virtual TArray Tick(float DeltaSeconds) const override; + virtual void HandleAction(const FString& CheckId, const FString& ActionId) const override; +}; diff --git a/Source/ThirdwebEditor/Public/ThirdwebEditorCommands.h b/Source/ThirdwebEditor/Public/ThirdwebEditorCommands.h index d27c5eb..71106b7 100644 --- a/Source/ThirdwebEditor/Public/ThirdwebEditorCommands.h +++ b/Source/ThirdwebEditor/Public/ThirdwebEditorCommands.h @@ -3,7 +3,6 @@ #pragma once #include "ThirdwebEditorStyle.h" - #include "Framework/Commands/Commands.h" class FThirdwebEditorCommands : public TCommands @@ -13,23 +12,18 @@ class FThirdwebEditorCommands : public TCommands virtual void RegisterCommands() override; - static TSharedPtr GetOpenSettingsCommand() { return Get().OpenSettings; } - static TSharedPtr GetTakeScreenshotCommand() { return Get().TakeScreenshot; } - // ReSharper disable once CppConstValueFunctionReturnType - static const TArray> GetCommands() { return {Get().OpenSettings, Get().TakeScreenshot}; } - - template - static void ForEach(CallableT Callable) - { - // ReSharper disable once CppTooWideScopeInitStatement - const TArray> Commands = GetCommands(); - for (const TSharedPtr& Value : Commands) - { - Invoke(Callable, Value); - } - } - -protected: - TSharedPtr OpenSettings; + // Configuration + TSharedPtr OpenRuntimeSettings; + TSharedPtr OpenEditorSettings; + // Utilities TSharedPtr TakeScreenshot; + // Reference + TSharedPtr ViewDocumentation; + // Support + TSharedPtr AccessOfficialSupport; + TSharedPtr AccessCommunitySupport; + TSharedPtr ReportABug; + TSharedPtr IssueTracker; + // Footer + TSharedPtr ViewWebsite; }; diff --git a/Source/ThirdwebEditor/Public/ThirdwebEditorModule.h b/Source/ThirdwebEditor/Public/ThirdwebEditorModule.h index c26c1f8..d2e65e4 100644 --- a/Source/ThirdwebEditor/Public/ThirdwebEditorModule.h +++ b/Source/ThirdwebEditor/Public/ThirdwebEditorModule.h @@ -2,24 +2,42 @@ #pragma once +#include "Containers/Ticker.h" #include "Modules/ModuleManager.h" - class FThirdwebEditorModule : public IModuleInterface { public: - /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; - -private: - void RegisterMenus(); -public: - static void OpenSettingsButtonClicked(); + bool Tick(float DeltaSeconds); + + void ProcessLogMessage(const FString& InLogLevel, const FString& Category, const FString& Message); + void ProcessCustomSignal(const FString& Context, const FString& SignalId); - static void TakeScreenshotButtonClicked(); private: - TSharedPtr CommandList; + void RegisterMenus(); + + // Configuration + static void OpenRuntimeSettings(); + static void OpenEditorSettings(); + // Utilities + static void TakeScreenshot(); + // Reference + static void ViewDocumentation(); + // Support + static void AccessOfficialSupport(); + static void AccessCommunitySupport(); + static void ReportABug(); + static void IssueTracker(); + // Footer + static void ViewWebsite(); + + static TSharedRef GenerateOnlineSettingsMenu(TSharedRef InCommandList); + + TSharedPtr CommandList; + TSharedPtr CheckEngine; + FTSTicker::FDelegateHandle TickerHandle; }; diff --git a/Source/ThirdwebEditor/Public/ThirdwebEditorSettings.h b/Source/ThirdwebEditor/Public/ThirdwebEditorSettings.h index c3c6b3e..5e266bf 100644 --- a/Source/ThirdwebEditor/Public/ThirdwebEditorSettings.h +++ b/Source/ThirdwebEditor/Public/ThirdwebEditorSettings.h @@ -57,6 +57,9 @@ class THIRDWEBEDITOR_API UThirdwebEditorSettings : public UDeveloperSettings UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, DisplayName="Timeout", meta=(EditCondition="!bScreenshotDisableNotifications", EditConditionHides), Category="Screenshots|Notifications") int32 ScreenshotNotificationTimeoutSeconds; + UPROPERTY(Config, EditAnywhere, DisplayName="Hide Dropdown In Editor Toolbar", Category=Tools) + bool bHideDropdownInEditorToolbar; + public: static bool NotificationsEnabled() { return !Get()->bScreenshotDisableNotifications; } static FInputChord GetScreenshotShortcut() { return Get()->ScreenshotShortcut; } @@ -69,7 +72,8 @@ class THIRDWEBEDITOR_API UThirdwebEditorSettings : public UDeveloperSettings static FDirectoryPath GetScreenshotSaveDirectory() { return Get()->ScreenshotSaveDirectory; } static EThirdwebEditorBlueprintImageFormat GetScreenshotFormat() { return Get()->ScreenshotFormat; } static float GetScreenshotQuality(); - + static bool HideDropdownInEditorToolbar() { return Get()->bHideDropdownInEditorToolbar; } + /** Convenience Getter */ static UThirdwebEditorSettings* Get() { return GetMutableDefault(); } }; diff --git a/Source/ThirdwebEditor/Public/ThirdwebEditorUtils.h b/Source/ThirdwebEditor/Public/ThirdwebEditorUtils.h new file mode 100644 index 0000000..30f9b38 --- /dev/null +++ b/Source/ThirdwebEditor/Public/ThirdwebEditorUtils.h @@ -0,0 +1,9 @@ +// Copyright (c) 2024 Thirdweb. All Rights Reserved. + +#pragma once + +namespace ThirdwebEditorUtils +{ + template + extern void OpenDeveloperSettings(); +} diff --git a/Thirdweb.uplugin b/Thirdweb.uplugin index 9b72ce6..bfaaef7 100644 --- a/Thirdweb.uplugin +++ b/Thirdweb.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, - "Version": 200, - "VersionName": "2.0.0-dev", + "Version": 184, + "VersionName": "1.8.4", "FriendlyName": "Thirdweb SDK", "Description": "ThirdwebSDK for Unreal Engine", "Category": "Thirdweb", @@ -12,7 +12,7 @@ "SupportURL": "https://thirdweb.com/support", "CanContainContent": true, "IsBetaVersion": false, - "IsExperimentalVersion": true, + "IsExperimentalVersion": false, "Installed": false, "EditorCustomVirtualPath": "Thirdweb", "Modules": [