// Copyright (c) 2026 Juhapekka Piiroinen // All Rights Reserved. // AmoosedWeb project: https://amoosed.com #pragma once #include "CoreMinimal.h" #include "Subsystems/GameInstanceSubsystem.h" #include "AmoosedWebSubsystem.generated.h" struct FAmoosedNativeSession; // Metadata from one fully verified catalogue entry. Content is still fetched // separately and must be treated as data by the consuming game. USTRUCT(BlueprintType) struct FAmoosedCatalogueItem { GENERATED_BODY() UPROPERTY(BlueprintReadOnly, Category = "AmoosedWeb") FString Id; UPROPERTY(BlueprintReadOnly, Category = "AmoosedWeb") FString Kind; UPROPERTY(BlueprintReadOnly, Category = "AmoosedWeb") FString ResourcePath; }; DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAmoosedCatalogueRefreshed, bool, bSuccess, int64, Revision, const FString&, Message); DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAmoosedItemFetched, bool, bSuccess, const FString&, OutputPath, const FString&, Message); // Owns one verified native session per game instance. Native I/O runs on a // worker thread; Blueprint events are delivered on the game thread. UCLASS(BlueprintType) class AMOOSEDWEB_API UAmoosedWebSubsystem final : public UGameInstanceSubsystem { GENERATED_BODY() public: virtual void Deinitialize() override; UFUNCTION(BlueprintCallable, Category = "AmoosedWeb") void Configure(const FString& InSiteOrigin, const FString& InGameId, const FString& InGameVersion); UFUNCTION(BlueprintPure, Category = "AmoosedWeb") FString GetSiteOrigin() const { return SiteOrigin; } UFUNCTION(BlueprintPure, Category = "AmoosedWeb") FString GetGameId() const { return GameId; } UFUNCTION(BlueprintPure, Category = "AmoosedWeb") FString GetGameVersion() const { return GameVersion; } UFUNCTION(BlueprintCallable, Category = "AmoosedWeb") bool RefreshHostedCatalogue(); // CredentialFile is an absolute path to a trusted .amwkey or packed ICO. // The file is read on the worker thread and is never sent through a delegate. UFUNCTION(BlueprintCallable, Category = "AmoosedWeb") bool RefreshExternalCatalogue(const FString& CredentialFile); // FileName must be a single file name. Output is under Saved/AmoosedWeb. UFUNCTION(BlueprintCallable, Category = "AmoosedWeb") bool FetchPublicItem(const FString& ItemId, const FString& FileName); // MemberKeyFile is an absolute path to a private 64-hex-character key file. // The key is read on a worker thread and never included in Blueprint events. UFUNCTION(BlueprintCallable, Category = "AmoosedWeb") bool FetchMemberItem(const FString& ItemId, const FString& FileName, const FString& MemberKeyFile); UFUNCTION(BlueprintCallable, Category = "AmoosedWeb") void CancelActiveOperation(); UFUNCTION(BlueprintPure, Category = "AmoosedWeb") int64 GetCatalogueRevision() const { return CatalogueRevision; } UFUNCTION(BlueprintPure, Category = "AmoosedWeb") TArray GetItemIds() const { return ItemIds; } UFUNCTION(BlueprintPure, Category = "AmoosedWeb") TArray GetCatalogueItems() const { return CatalogueItems; } UFUNCTION(BlueprintPure, Category = "AmoosedWeb") FString GetLastError() const { return LastError; } // Numeric amw_preview_* status. Zero means the last completed call succeeded. // Match this code with the public Preview C ABI status reference. UFUNCTION(BlueprintPure, Category = "AmoosedWeb") int32 GetLastStatusCode() const { return LastStatusCode; } UFUNCTION(BlueprintPure, Category = "AmoosedWeb") bool IsOperationActive() const { return bOperationActive; } UPROPERTY(BlueprintAssignable, Category = "AmoosedWeb") FAmoosedCatalogueRefreshed OnCatalogueRefreshed; UPROPERTY(BlueprintAssignable, Category = "AmoosedWeb") FAmoosedItemFetched OnItemFetched; private: bool BeginCatalogueRefresh(const FString& CredentialFile, bool bExternal); bool BeginItemFetch(const FString& ItemId, const FString& FileName, const FString& MemberKeyFile, bool bMember); FString SiteOrigin; FString GameId; FString GameVersion; FString LastError; int32 LastStatusCode = 0; bool bOperationActive = false; int64 CatalogueRevision = -1; TArray ItemIds; TArray CatalogueItems; TSharedPtr NativeSession; };